When a program emits logs inside of a transaction, each log is effectively just added to a single, flat array of strings. So in order to actually determine the instruction that caused a program to emit a specific log is sort of difficult.
The way that the Solana explorer parses and display a transaction's logs is probably the best way to do. The explorer's parsing takes a few assumptions in its parsing:
- all the transaction logs are a simple array of strings
- each "simple string" log contains the address of the program that created the log (this is true because how the Solana rust code actually formats the logs)
- instructions within a Solana transaction are executed atomically (i.e. in order). this is how the Solana runtime is designed and always true
- at the completion of each program execution, there will always be a "success" message (the runtime does this automatically, and does not require the program developer to add this message). (this is the the message to pay extra attention too when processing the logs)
So if you loop over the simple array of string log values (type String[]
):
- each log will have the program (
programId
) that executed it towards the beginning of the string
- when the programId changes, you know the runtime is processing a different program
- when the log is a "success" log, you know the program completed successfully. aka the instruction completed successfully (either the cpi or the non-cpi instructions)
Using this knowledge, you can loop over the array of string values, parsing out the instructions from a specific program and specific instruction within a transaction.
Note: there is potentially an edge case not covered by the explorer's parser. I believe that if a program decides to send extra logs that look like they were formatted similar enough, it could trick the explorer's simple parser to miss display the logs in the UI.