Sometimes questions are posted here that would best be answered by helping the questioner learn how to do their own debugging. (Remember that proverb?) I'd like to have something to link to on this site for that purpose.

So please provide a step by step recipe for how to output basic debug information from Apex code in triggers or controllers for inexperienced Salesforce developers.

Also see How do I start to debug my own Visualforce/JavaScript?.


If/when this Force.com IDE Debugger. New feature? appears Apex may/should become as easy to debug as other languages.

PPS Looks like Winter '16 has the interactive debugger for Apex though it is something that has to be opted in to and paid for (and has yet to be widely tried). See Apex debugging in Winter '16 - how well does it work?

  • 5
    This may not be a step-by-step recipe, but the system.debug() method is your best friend. Literally pepper your code with these calls, since when you test your code in a sandbox and have debug logging on (logging level = debug) every message is written into the debug log. I use it to see variable values before and after actions.
    – akarnid
    Commented May 21, 2014 at 10:44
  • 1
    Let's say you have written a trigger and want to know specific values in the code to find out why it isn't working, and you have set a few debug statements in the trigger code. 1. Save the code. 2. Go to Monitor -> Logs -> Debug Logs 3. Click the New button in Monitored Users. Find your user and select it and save. 4. Trigger the trigger somehow (if it fires on a field update, create a test record and update the field and click Save). 5. Go to Monitor -> Logs -> Debug Logs again. Now you will see a couple of new logs for your user. You can click 'View' and you should see your debug info
    – akarnid
    Commented May 21, 2014 at 11:49
  • 1
    @akarnid that information would be more useful (and readable) as an answer rather than a comment.
    – BarCotter
    Commented May 21, 2014 at 12:29
  • 1
    When using system.debug() to output values from your code, add a prefix that doesn't occur elsewhere in your output. That way, you can filter for just the output of your own system.debug() calls (eg. "MyNewCodeMar2014-" as prefix gives "MyNewCode2014-#of cases: 23" as output)
    – Jagular
    Commented May 21, 2014 at 14:50
  • 1
    The proverb if anybody doesn't know it: 'Give a man a fire and he's warm for a day, but set fire to him and he's warm for the rest of his life.' - Terry Pratchett.
    – Jim W
    Commented May 28, 2016 at 19:52

11 Answers 11


Here is how I debug my issues. In most cases I would only make it to step 2 before I solve my problem.

Step 1: Explain it to your Rubber Duck

I try explain what my code is doing to my rubber duck. A lot of the time I figure out the problem when I'm explaining the code to someone else. (If exceptions are involved, take a look at your code at the line numbers reported and consider what could have generated the exception.)

Step 2: System Debugs

Debug log recording in Setup is turned on for your User via: Setup > Monitoring > Debug logs. See here for more information.

I would place temporary debug statements such as System.Debug('>>>> the value of x is ' + x); within my code to make sure that the code is executing the way I think its working. Individual SObjects, Maps of SObjects and Lists of SObjects can all be appended and that shows you all populated SObject fields.

The >>>> is a unique string that usually doesn't appear anywhere else in the log files. This allows me to quickly find my debug output. (Logs are truncated after 2M bytes of output - there are work-arounds for this.)

Step 3: Use the Developer Console

The Developer Console is a great tool for debugging. You do the following things (and more) with the developer console

  • View Logs. This is another way of viewing debug output.
  • Execute SOQL. This can be used to verify that the SOQL in your code is returning the correct information
  • Execute Anonymous. Apex code can be run directly from the dev console
  • Checkpoints can be added in the code (maximum of 5 per class), which will allow you to stop the code executing and to see variable information.

See Josh Kaplans YouTube video for more info on the dev console

Step 4: Create a Unit Test

A unit test is a great way to figure out what is going on with a piece of code. It allows you to:

  • Execute your code in an environment with no other data
  • Create test data that you can use over and over again.
  • Use asserts to check your code e.g. System.assert(contacts.size() > 0); or System.assertEquals(expectedX, actualX);

Step 5: Take a break

I find myself getting frustrated when I have been looking at the same problem for a long period of time. This does not help solve the problem, it can actually make it harder to clearly see what is going on in the code. Take a break for a while and you may find that when you look at the problem again it makes more sense.

Step 6: Ask for help

If you have done all the steps above and reached this point then you most likely need help. This is where a colleague or stack exchange come in. Make sure when asking a question that you clearly state your problem, provide enough information to make it understandable to others and if you are providing a code sample make sure that it is formatted and easy to read.

  • 21
    Nice. +1 for rubber duck; she never gets enough attention. If you want nothing but debug logs, turn all your log settings to the "error" level, then use System.debug(LoggingLevel.ERROR, someValue). You'll get small logs (mere Kb) with just the info you requested.
    – sfdcfox
    Commented May 21, 2014 at 15:21
  • 13
    I try to avoid the use of debug statements like the one mentioned using a unique string like >>> unless I plan to remove them when I am done troubleshooting, since they're not helpful to anyone else. Instead, I attempt to create useful, verbose debug statements that can live in the code forever and will probably be beneficial to other devs/admins troubleshooting at a later time. For instance: system.debug('Found a matching record using the postal code prefix [' + aWrapper.postalCodePrefix + '] but assignee UserId [' + postalCodeOwnerAssigment.Assignee__c + '] is an inactive User.');
    – Mark Pond
    Commented May 21, 2014 at 18:34
  • 3
    I agree Mark. I have changed my answer to say that it is a temporary debug statement
    – BarCotter
    Commented May 22, 2014 at 10:00
  • 1
    I usually print apex:pageMessages in Visualforce page or use Trigger.addError in triggers for debugging, they are faster than looking at debug logs sometimes.
    – Raul
    Commented Feb 20, 2017 at 19:05
  • people worry about me talking to myself especially when working - actually i talk to rubber duck which can be my other self.
    – unidha
    Commented Oct 31, 2018 at 7:59

I've added an Apex debugger to Illuminated Cloud that allows you to step through code, see variable values, etc. It uses information from debug logs and checkpoints to "replay" the executed process, and it's tightly integrated into other IDE features such as unit test execution, anonymous Apex, and the integrated log viewer.

For small-to-medium processes, the experience is very similar to an interactive debugger. For larger processes that would exceed the maximum debug log size, it still allows you to stop at registered checkpoints and review heap dump and action script results in the IDE.

You can find out more here:


And here's a short video showing it in use:


  • So not using the hard to get and expensive Apex debugger APIs? Nice. I'll pass this on to my colleague who is our Illuminated Cloud advocate.
    – Keith C
    Commented Apr 22, 2016 at 21:04
  • 2
    That's correct. It doesn't use anything you don't already have, even in a completely free developer edition org. Obviously it's going to be subject to the limitations of the features that it's using, in particular the maximum debug log size of 2MB and the formatting of some of the variable values (though checkpoints help with both of these), but I'm quite pleased with how it's turned out. Hopefully others will find it useful as well, especially given the limited options we've all had available to us! Commented Apr 22, 2016 at 21:13

I use following techniques:

1) Developer console : Go to [User Name] -> Developer Console. It will open a popup for you where you can run your code, run SOQL and monitor debug logs of executed code.

2) Debug Logs: Sometimes jobs are executed in the background and so monitoring from the developer console becomes difficult. In such cases, we can use Setup -> Monitor -> Logs -> Debug Logs to monitor logs. Also, you can monitor at the user/class/trigger level and filter by log level to get to reduce noise from log.

3) Send email you can send email from apex code to notify the status of log output when log is truncated due to huge size.

4) Log object(Custom object) you can create a custom object to insert the log status as a new record based on custom setting. This is very useful if you have a managed package and customer face any intermediate issue. In such cases you can set the custom setting for that customer and check the records log object to see what was causing the issue.

5) anonymous block you can use anonymous block to debug/execute specific code and verify if its working fine.

Here are some helpful reference links :




Make an APEX class that can run unit tests and print output:

  1. Login to salesforce (I'm on development side).
  2. Choose Setup and expand the Develop category.
  3. Choose "Apex Classes".
  4. Choose "New" button to make a new class. (You may need to add permissions).
  5. Paste in this code:

    public with sharing class Test_Foobars {
      static testMethod void testFoobars(){
        System.debug(Logginglevel.INFO, 'You forgot to refromulate the subspace responders!');
        System.assertEquals('uranium235', 'uranium238');
  6. Save it as "Test_Foobars"

  7. You have to turn on Debugging for your user. Go back to salesforce and click "Setup".
  8. Expand the "Logs" category.
  9. Click the "Debug Logs" item.
  10. Under "Monitored Users", click "New".
  11. Type in your first name, click the magnifying glass to select yourself from the list.
  12. choose Save.
  13. Go to the Apex class you created earlier. Setup->Develop->Apex classes->Test_Foobars
  14. Under the "Apex Class Detail" category choose "Run Test". Wait for it to be done.
  15. Go back to Setup->Logs->Debug Logs Look under the "Debug Logs" header.
  16. Sort by time run descending, you should see the output of your test.
  17. Next to your name, click "View".
  18. Under the "log" section you see this:

        10:11:27.565 (565036214)|EXECUTION_STARTED
        10:11:27.565 (565106209)|CODE_UNIT_STARTED|[EXTERNAL]|01pg00000001qxv|Test_Foobars.testFoobars
        10:11:27.565 (565925400)|METHOD_ENTRY|[2]|01pg00000001qxv|Test_Foobars.Test_Foobars()
        10:11:27.567 (567067604)|SYSTEM_MODE_ENTER|false
        10:11:27.567 (567080797)|SYSTEM_MODE_EXIT|false
        10:11:27.567 (567091913)|METHOD_EXIT|[2]|Test_Foobars
        10:11:27.567 (567139033)|SYSTEM_MODE_ENTER|false
        10:11:27.567 (567268931)|SYSTEM_METHOD_ENTRY|[4]|System.debug(APEX_OBJECT, ANY)
        10:11:27.567 (567307215)|USER_DEBUG|[4]|INFO|You forgot to refromulate the subspace responders!
        10:11:27.567 (567316940)|SYSTEM_METHOD_EXIT|[4]|System.debug(APEX_OBJECT, ANY)
        10:11:27.567 (567334906)|SYSTEM_METHOD_ENTRY|[5]|System.assertEquals(ANY, ANY)
        10:11:27.567 (567412068)|EXCEPTION_THROWN|[5]|System.AssertException: Assertion Failed: Expected: uranium235, Actual: uranium238
        10:11:27.567 (567491823)|SYSTEM_METHOD_EXIT|[5]|System.assertEquals(ANY, ANY)
        10:11:27.567 (567507919)|SYSTEM_MODE_EXIT|false
        10:11:27.567 (567565312)|FATAL_ERROR|System.AssertException: Assertion Failed: Expected: uranium235, Actual: uranium238
        Class.Test_Foobars.testFoobars: line 5, column 1
        10:11:27.567 (567580474)|FATAL_ERROR|System.AssertException: Assertion Failed: Expected: uranium235, Actual: uranium238
        Class.Test_Foobars.testFoobars: line 5, column 1
        10:11:27.227 (567602387)|CUMULATIVE_LIMIT_USAGE
          Number of SOQL queries: 0 out of 100
          Number of query rows: 0 out of 50000
          Number of SOSL queries: 0 out of 20
          Number of DML statements: 0 out of 150
          Number of DML rows: 0 out of 10000
          Maximum CPU time: 0 out of 10000
          Maximum heap size: 0 out of 6000000
          Number of callouts: 0 out of 100
          Number of Email Invocations: 0 out of 10
          Number of future calls: 0 out of 50
          Number of queueable jobs added to the queue: 0 out of 50
          Number of Mobile Apex push calls: 0 out of 10
        10:11:27.567 (567632141)|CODE_UNIT_FINISHED|Test_Foobars.testFoobars
        10:11:27.570 (570273184)|EXECUTION_FINISHED
  19. There you can see the debug output (buried in it) and the results of the unit test.

It's hard to use, hard to find and configure, hard to turn on, but once it's working it's not so bad.

Now you can salesforce good. Dude, do you even apex? Y U NO APEX??


The mechanics of how to get debugging information have been covered effectively in the other answers. This is more of a 'how-to fish guide' to use your metaphor.

  1. You start with a symptom of the problem which could be anything: incorrect data in UI, exceptions, error log. I call this the 'tail of the snake'.
  2. Now you have to pull on the snake's tail to follow the chain of causation, in several iterations until you get to the head of the snake -- the root of the problem. Here is how you pull on the tail:
    • Write a test that consistently reproduces the problem. This will help you iterate and also verify that you've fixed the problem.
    • Find the line of code that printed the error, threw the exception or wrote the bad data to the UI.
    • In the method you've identified, add checkpoints and trace (System.debug) statements to determine how you got to that line of code. If you are in an exception handler, which line of code in the try block threw the exception? If it's not an exception, you will need to trace the data that caused your code hit the branch of code that is symptomatic of the problem -- the data may be a method parameter or DB data.
    • Execute the test.
    • Inspect the logs. If you can't figure out the cause of the bug, you need to trace the problem higher up the chain of causation following a 'bad value'. You will need to add more checkpoint and trace statements and repeat this cycle until you get to the exact line of offending code.
    • Once you think you fixed the code, run your test again and see if it passes.

Like with fishing, the more you practice the better you will get over time.


You could try using the Salesforce Streaming Debugger application to get log the output that will aid in debugging. This essentially makes callouts to a Heroku app for logging.

  • 1
    I have always found, Salesforce debugger doing the Job effortlessly. Just copy RestLoggerClass into Apex and call RestLogger.debug('name', data);
    – Kiba
    Commented Sep 8, 2015 at 19:38
  • Keeping in mind that you cannot do any DML prior to the debugging which limits the implementation dramatically
    – Eric
    Commented Sep 8, 2015 at 19:54
  • Not sure it is the best security to stream logs to a random Heroku app...
    – Shanerk
    Commented May 25, 2023 at 13:47

Following up on the other answers, if you do use System.Debug and you use VSCode, you can view the debug statements in real time via the terminal. Execute this command in the VSCode terminal window (showing USER_DEBUG and FATAL_ERROR lines only):

sf apex tail log --color | grep 'USER_DEBUG\|FATAL_ERROR'

and in Windows PowerShell

sf apex tail log --color | select-string -pattern USER_DEBUG,FATAL_ERROR

The debug logs will show up in your Terminal window. Note the old command: sfdx force:apex:log:tail

See docs: https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_apex_commands_unified.htm#cli_reference_apex_tail_log_unified

  • 1
    How come no one told me about this. Thank you very much Shane. Commented Jun 19, 2020 at 20:44
  • 1
    @Shane Almost a year ago i wanted to switch from IntelliJ Illuminated Cloud to VS Code ... but i was baffled with the way VS code handled debug logs - that you explicitly have to retrieve them - so i stuck with IntelliJ. But seeing your Comment here any trying out this feature - it gives me again the courage to give VS Code another shot. THAAAANKS . How come this statement is no where easily found on the internet??!!! Anyways THANKS
    – owsata
    Commented Jun 26, 2020 at 9:40

There is a new replacement for Apex Debugger coming this Summer in beta called Apex Replay debugger that supports a variety of consoles, not just Eclipse like the current version. Particularly, it now better supports SFDX and VS Code.

You can see a demo of it starting at around 32:00 in the following video from TrailheaDX: https://developer.salesforce.com/trailheadx/sessions?q=a2q3A000001py7cQAA#march-28


Just in case there weren't already enough "log replay debuggers" out there, I wrote another for vscode (dependant on mavensmate).

It's still pretty much in alpha, but is stable enough that you can set breakpoints and step through your code with decent variable inspection.

Best of all, it's free and open source so you can extend it and help me fix bugs!


Salesforce wrote an official log replay debugger as part of their vscode extension. I recommending using that one if possible (it will require that you use the new SFDX format thought).

Mine has not been updated in sometime (since I found out about the official one).


Just to add - if you're looking at the Apex debugger tying into VS Code (either new one or existing), you won't be able to do this with a trial dev hub.

Just since you mentioned it in your PPS.


There are a few methods:

Debug Logs

  1. Add System.debug('Message: ' + variable); to your Apex code.
  2. Go to Setup > Debug Logs > New
  3. Select Track Entity and Logs Level.
  4. Execute code (anonymous apex or via UI)
  5. Refresh Setup > Debug Logs page and find your log.
Execute Anonymous: System.debug('Hello World!');
16:06:58.18 (18043585)|USER_INFO|[EXTERNAL]|005D0000001bYPN|[email protected]|
    Pacific Standard Time|GMT-08:00
16:06:58.18 (18348659)|EXECUTION_STARTED
16:06:58.18 (18383790)|CODE_UNIT_STARTED|[EXTERNAL]|execute_anonymous_apex
16:06:58.18 (23822880)|HEAP_ALLOCATE|[72]|Bytes:3
16:06:58.18 (24271272)|HEAP_ALLOCATE|[77]|Bytes:152
16:06:58.18 (24691098)|HEAP_ALLOCATE|[342]|Bytes:408
16:06:58.18 (25306695)|HEAP_ALLOCATE|[355]|Bytes:408
16:06:58.18 (25787912)|HEAP_ALLOCATE|[467]|Bytes:48
16:06:58.18 (26415871)|HEAP_ALLOCATE|[139]|Bytes:6
16:06:58.18 (26979574)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:1
16:06:58.18 (27384663)|STATEMENT_EXECUTE|[1]
16:06:58.18 (27414067)|STATEMENT_EXECUTE|[1]
16:06:58.18 (27458836)|HEAP_ALLOCATE|[1]|Bytes:12
16:06:58.18 (27612700)|HEAP_ALLOCATE|[50]|Bytes:5
16:06:58.18 (27768171)|HEAP_ALLOCATE|[56]|Bytes:5
16:06:58.18 (27877126)|HEAP_ALLOCATE|[64]|Bytes:7
16:06:58.18 (49244886)|USER_DEBUG|[1]|DEBUG|Hello World!
16:06:58.49 (49590539)|CUMULATIVE_LIMIT_USAGE
16:06:58.49 (49590539)|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 0 out of 100
  Number of query rows: 0 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 0 out of 150
  Number of DML rows: 0 out of 10000
  Maximum CPU time: 0 out of 10000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

16:06:58.49 (49590539)|CUMULATIVE_LIMIT_USAGE_END

16:06:58.18 (52417923)|CODE_UNIT_FINISHED|execute_anonymous_apex
16:06:58.18 (54114689)|EXECUTION_FINISHED

Apex Replay Debugger

  1. Set line breakpoints. Open a .cls or .trigger file and click the column to the left of the line numbers.
  2. Ctrl + Shift + P > SFDX: Turn On Apex Debug Log for Replay Debugger.
  3. Reproduce the issue or Execute Anonymous Apex.
  4. Ctrl + Shift + P > SFDX: Get Apex Debug Logs.
  5. Select your log from the list.
  6. Ctrl + Shift + P > Run SFDX: Launch Apex Replay Debugger with Current File.
  7. Code will stop on your breakpoints (point #1).

enter image description here

SFDX and Streaming Logs

  • Terminal > sfdx force:apex:log:tail --color - SFDX will now start outputting the debug logs from your org for your user line by line into the terminal window. This is useful, we can now inspect our debug logs as they come through in the terminal window to review the information within to find our bug.

sfdx force:apex:log:tail --color -d OWN_DEBUG_LEVEL

sfdx force:apex:log:tail --color -d DEV_DEBUG | grep USER_DEBUG - display only System.debug statements (USER_DEBUG)

Apex Interactive Debugger

  1. Create Permission Set (e.g Debug Apex) with System Permission > Enable Debug Apex.
  2. Assign Debug Apex Permission Set to your user.
  3. In Visual Studio Code click the bug icon in the panel on the right side. Select Launch Apex Debugger from the picklist and click the green triangle to start.
  4. Set line breakpoints. Open a .cls or .trigger file and click the column to the left of the line numbers.
  5. Upload your checkpoints to your org Ctrl + Shift + P > SFDX: Update Checkpoints in Org.
  6. Reproduce the issue or execute the code.
  7. Code will be stopped on your breakpoints (point #5).

enter image description here

You can find more details here https://beyondthecloud.dev/blog/apex-debugging

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .