|
| 1 | +# Logging CorDapp [<img src="../../webIDE.png" height=25 />](https://ide.corda.net/?folder=/home/coder/samples-java/Basic/yo-cordapp) |
| 2 | + |
| 3 | + |
| 4 | +## Custom Logging |
| 5 | + |
| 6 | +This is a modified version of the original yo cordapp with some additions to use custom log4j2 configurations. |
| 7 | + |
| 8 | + |
| 9 | +The primary example we've implemented here is json logging which is configured in `config/dev/log4j2.xml`. |
| 10 | + |
| 11 | +This gives us the ability to use Log4j thread contexts to log arbitrary objects or data points in json format. |
| 12 | + |
| 13 | +In this example not only do the node logs output in json but we can add arbitrary key value pairs as well. |
| 14 | + |
| 15 | +```java |
| 16 | + // here we have our first opportunity to log out the contents of the flow arguments. |
| 17 | + ThreadContext.put("initiator", me.getName().toString()); |
| 18 | + ThreadContext.put("target", target.getName().toString()); |
| 19 | + // publish to the log with the additional context |
| 20 | + logger.info("Initializing the transaction."); |
| 21 | +``` |
| 22 | + |
| 23 | +When we log this informational message, it gets output along with the other key value pairs we've specified. |
| 24 | +This can be quite powerful if you're looking to produce a consumable output stream to a log aggregator like splunk. |
| 25 | + |
| 26 | +You can end up getting log feeds in json that look something like this: |
| 27 | + |
| 28 | +```json |
| 29 | +{"epochSecond":1612369055,"nanoOfSecond":12000000},"thread":"main","level":"INFO","loggerName":"net.corda.node.internal.Node","message":"Vendor: Corda Open Source","endOfBatch":true,"loggerFqcn":"org.apache.logging.slf4j.Log4jLogger","threadId":1,"threadPriority":5} |
| 30 | +{"instant":{"epochSecond":1612369055,"nanoOfSecond":12000000},"thread":"main","level":"INFO","loggerName":"net.corda.node.internal.Node","message":"Release: 4.6","endOfBatch":false,"loggerFqcn":"org.apache.logging.slf4j.Log4jLogger","threadId":1,"threadPriority":5} |
| 31 | +{"instant":{"epochSecond":1612369055,"nanoOfSecond":12000000},"thread":"main","level":"INFO","loggerName":"net.corda.node.internal.Node","message":"Platform Version: 8","endOfBatch":false,"loggerFqcn":"org.apache.logging.slf4j.Log4jLogger","threadId":1,"threadPriority":5} |
| 32 | +{"instant":{"epochSecond":1612369055,"nanoOfSecond":12000000},"thread":"main","level":"INFO","loggerName":"net.corda.node.internal.Node","message":"Revision: 85e387ea730d9be7d6dc2b23caba1ee18305af74","endOfBatch":false,"loggerFqcn":"org.apache.logging.slf4j.Log4jLogger","threadId":1,"threadPriority":5} |
| 33 | +{"instant":{"epochSecond":1612369055,"nanoOfSecond":13000000},"thread":"main","level":"INFO","loggerName":"net.corda.node.internal.Node","message":"PID: 94369","endOfBatch":false,"loggerFqcn":"org.apache.logging.slf4j.Log4jLogger","threadId":1,"threadPriority":5} |
| 34 | +``` |
| 35 | + |
| 36 | + |
| 37 | +## Yo CorDapp Concepts |
| 38 | + |
| 39 | +In the original yo application, the app sent what is essentially a nudge from one endpoint and another. |
| 40 | + |
| 41 | +In corda, we can use abstractions to accomplish the same thing. |
| 42 | + |
| 43 | + |
| 44 | +We define a [state](https://training.corda.net/key-concepts/concepts/#states) (the yo to be shared), define a [contract](https://training.corda.net/key-concepts/concepts/#contracts) (the way to make sure the yo is legit), and define the [flow](https://training.corda.net/key-concepts/concepts/#flows) (the control flow of our cordapp). |
| 45 | + |
| 46 | +### States |
| 47 | +We define a [Yo as a state](./contracts/src/main/java/net/corda/examples/yo/states/YoState.java#L31-L35), or a corda fact. |
| 48 | + |
| 49 | +```java |
| 50 | + public YoState(Party origin, Party target) { |
| 51 | + this.origin = origin; |
| 52 | + this.target = target; |
| 53 | + this.yo = "Yo!"; |
| 54 | + } |
| 55 | +``` |
| 56 | + |
| 57 | + |
| 58 | +### Contracts |
| 59 | +We define the ["Yo Social Contract"](./contracts/src/main/java/net/corda/examples/yo/contracts/YoContract.java#L21-L32), which, in this case, verifies some basic assumptions about a Yo. |
| 60 | + |
| 61 | +```java |
| 62 | + @Override |
| 63 | + public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException { |
| 64 | + CommandWithParties<Commands.Send> command = requireSingleCommand(tx.getCommands(), Commands.Send.class); |
| 65 | + requireThat(req -> { |
| 66 | + req.using("There can be no inputs when Yo'ing other parties", tx.getInputs().isEmpty()); |
| 67 | + req.using("There must be one output: The Yo!", tx.getOutputs().size() == 1); |
| 68 | + YoState yo = tx.outputsOfType(YoState.class).get(0); |
| 69 | + req.using("No sending Yo's to yourself!", !yo.getTarget().equals(yo.getOrigin())); |
| 70 | + req.using("The Yo! must be signed by the sender.", command.getSigners().contains(yo.getOrigin().getOwningKey())); |
| 71 | + return null; |
| 72 | + }); |
| 73 | + } |
| 74 | + |
| 75 | +``` |
| 76 | + |
| 77 | + |
| 78 | +### Flows |
| 79 | +And then we send the Yo [within a flow](./workflows/src/main/java/net/corda/examples/yo/flows/YoFlow.java#L59-L64). |
| 80 | + |
| 81 | +```java |
| 82 | + Party me = getOurIdentity(); |
| 83 | + Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0); |
| 84 | + Command<YoContract.Commands.Send> command = new Command<YoContract.Commands.Send>(new YoContract.Commands.Send(), ImmutableList.of(me.getOwningKey())); |
| 85 | + YoState state = new YoState(me, target); |
| 86 | + StateAndContract stateAndContract = new StateAndContract(state, YoContract.ID); |
| 87 | + TransactionBuilder utx = new TransactionBuilder(notary).withItems(stateAndContract, command); |
| 88 | +``` |
| 89 | + |
| 90 | +On the receiving end, the other corda node will simply receive the Yo using corda provided subroutines, or subflows. |
| 91 | + |
| 92 | +```java |
| 93 | + return subFlow(new ReceiveFinalityFlow(counterpartySession)); |
| 94 | +``` |
| 95 | + |
| 96 | + |
| 97 | +## Usage |
| 98 | + |
| 99 | + |
| 100 | +### Pre-Requisites |
| 101 | + |
| 102 | +See https://docs.corda.net/getting-set-up.html. |
| 103 | + |
| 104 | + |
| 105 | +### Running the CorDapp |
| 106 | + |
| 107 | +Open a terminal and go to the project root directory and type: (to deploy the nodes using bootstrapper) |
| 108 | +``` |
| 109 | +./gradlew clean deployNodes |
| 110 | +``` |
| 111 | +Then type: (to run the nodes) |
| 112 | + |
| 113 | +``` |
| 114 | +./build/nodes/runnodes |
| 115 | +``` |
| 116 | + |
| 117 | +### Sending a Yo |
| 118 | + |
| 119 | +We will interact with the nodes via their specific shells. When the nodes are up and running, use the following command to send a |
| 120 | +Yo to another node: |
| 121 | + |
| 122 | +``` |
| 123 | + flow start YoFlow target: PartyB |
| 124 | +``` |
| 125 | + |
| 126 | +Where `NODE_NAME` is 'PartyA' or 'PartyB'. The space after the `:` is required. You are not required to use the full |
| 127 | +X500 name in the node shell. Note you can't sent a Yo! to yourself because that's not cool! |
| 128 | + |
| 129 | +To see all the Yo's! other nodes have sent you in your vault (you do not store the Yo's! you send yourself), run: |
| 130 | + |
| 131 | +``` |
| 132 | + run vaultQuery contractStateType: YoState |
| 133 | +``` |
| 134 | + |
| 135 | +### Viewing custom logs |
| 136 | + |
| 137 | +This will depend on your cordapp setup, if you're running your corda nodes all you need to do is specify the particular config file. You can do that in a couple of ways. |
| 138 | + |
| 139 | + |
| 140 | +``` |
| 141 | +"-Dlog4j.configurationFile=logging-cordapp/build/resources/main/log4j2.xml" |
| 142 | +``` |
| 143 | + |
| 144 | +If you're running with the bootstrapped corda network you can run it by simply adding this argument to the result of the runnodes command. |
| 145 | + |
| 146 | + |
| 147 | +``` |
| 148 | +'cd "/Users/corda/logging-cordapp/build/nodes/PartyA" ; "/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/bin/java" "-Dcapsule.jvm.args=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006 -javaagent:drivers/jolokia-jvm-1.6.0-agent.jar=port=7006,logHandlerClass=net.corda.node.JolokiaSlf4jAdapter" "-Dname=PartyA" "-jar" "-Dlog4j.configurationFile=/Users/corda/logging-cordapp/build/resources/main/log4j2.xml" "/Users/corda/logging-cordapp/build/nodes/PartyA/corda.jar" ; and exit' |
| 149 | +
|
| 150 | +``` |
| 151 | + |
| 152 | + |
| 153 | + |
| 154 | +## Attribution |
| 155 | + |
| 156 | +This example was built in collaboration with [Splunk](https://splunk.com), and they have our thanks. |
| 157 | + |
| 158 | + |
| 159 | + |
| 160 | + |
| 161 | + |
0 commit comments