All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mats3.util.TraceId Maven / Gradle / Ivy

Go to download

Mats^3 Utilities - notably the MatsFuturizer, which provides a bridge from synchronous processes to the highly asynchronous Mats^3 services.

There is a newer version: 0.19.22-2024-11-09
Show newest version
package io.mats3.util;

import java.util.LinkedHashMap;
import java.util.Map.Entry;

import io.mats3.MatsEndpoint.ProcessContext;
import io.mats3.MatsInitiator.InitiateLambda;

/**
 * A small tool to produce an opinionated TraceId - an example of such a string could be
 * "Order.placeOrder:calcShipping[cid:123456][oid:654321]iu4m3p - since this class implements
 * {@link CharSequence}, it can be used directly as the 'traceId' argument in initiations.
 * 

* The tool accepts null for any of the three "mandatory" pieces "appShort", "initShort" and "reason". *

* Remember that TraceIds should be as short as possible! Be conservative with the lengths of these constituent * strings, and key names, and key values! Don't use UUIDs, they are typically both way too unique, and way too long * since they only use a limited alphabet. *

    *
  1. {@code appShort}: A short-form of the application name - i.e. which service started this Mats Flow.
  2. *
  3. {@code initShort}: A short-form of the InitiatorId - i.e. some clue as to where in the service's codebase this * originates.
  4. *
  5. {@code reason}: An element of "reason" behind for creating this Flow, either an element of "to", i.e. which * service this targets. Or if multiple flows originate from this initiatorId-point, then a "sub-init" element.
  6. *
  7. {@link #batchId(long) batchId(..)}: If this Mats Flow is a part of several that are sent out in a batch, then you * may employ this to group them together.
  8. *
*

* The special factory variants {@link #concat(String, String, String) concat(..)} and * {@link #fork(String, String, String) fork(..)} are used if you already have some kind of eventId or traceId which you * want to hook into the new Mats Flow. If you want to add contextual information on the existing eventId for a new * single Mats Flow, then you use concat(..) (uses a "+"). If this one single event leads to multiple new * flows, then you use fork(..) (uses a "|" - which is done automatically for you if you initiate new * messages from within a Mats Flow, i.e. inside a Stage with {@link ProcessContext#initiate(InitiateLambda) * processContext.initiate(..)}. *

* The only important thing with a TraceId is that it is unique, so that it is possible to uniquely find it in the * distributed logging system. All else is only relevant for understanding when you need to reason about a specific * flow, typically in a debugging scenario, e.g. why did this end up on a Dead Letter Queue. It is extremely nice to * then immediately understand quite a bit out from the TraceId alone. *

* Note: It is possible to extend this concept into domain-specific tools which add more specialized * {@link #add(String, long) add(key, value)}-variants in that the key is specified. E.g. if you in many contexts have * customerId and an orderId, it is nice if this always uses the same key "custId" (or "cid") and "orderId" (or "oid"), * instead of this varying between the initiating services. Thus you add a method customerId(value) and * orderId(value), which just forwards to the add-method. *

* Note: You can modify and reuse a TraceId object, e.g. change the 'reason' and perform a new initialization. Mats will * perform a {@link #toString()} on the instance when *

* There's a document about these identifiers at Mats3's github: * TraceIds and * InitiatorIds. * * @author Endre Stølsvik 2022-02-23 12:49 - http://stolsvik.com/, [email protected] */ public class TraceId implements CharSequence { public static TraceId create(String appShort, String initShort) { return new TraceId(appShort, initShort, null); } public static TraceId create(String appShort, String initShort, String reason) { return new TraceId(appShort, initShort, reason); } public static TraceId concat(String prefix, String appShort, String initShort) { TraceId traceId = new TraceId(appShort, initShort, null); traceId.concat(prefix); return traceId; } public static TraceId concat(String prefix, String appShort, String initShort, String reason) { TraceId traceId = new TraceId(appShort, initShort, reason); traceId.concat(prefix); return traceId; } public static TraceId fork(String prefix, String appShort, String initShort) { TraceId traceId = new TraceId(appShort, initShort, null); traceId.fork(prefix); return traceId; } public static TraceId fork(String prefix, String appShort, String initShort, String reason) { TraceId traceId = new TraceId(appShort, initShort, reason); traceId.fork(prefix); return traceId; } /** * Reset the 'reason' part. */ public TraceId reason(String reason) { _modified = true; _reason = reason; return this; } /** * Set the "batch" parameter, which always will be the first of the key-value props. */ public TraceId batchId(String batchId) { _modified = true; _batchId = batchId; return this; } /** * Set the "batch" parameter, which always will be the first of the key-value props. */ public TraceId batchId(long batchId) { return batchId(Long.toString(batchId)); } /** * Add a key-value property - the addition order will be kept. */ public TraceId add(String key, String value) { _modified = true; if (_keyValues == null) { _keyValues = new LinkedHashMap<>(); } _keyValues.put(key, value); return this; } /** * Add a key-value property - the addition order will be kept. */ public TraceId add(String key, long value) { return add(key, Long.toString(value)); } /** * Adds a "!" (exclamation) character in front of the TraceId, which tells Mats that it should not prepend with the * current TraceId in a Stage-Initiate setting - note that a leading exclamation character will be removed in any * case. */ public TraceId preventPrepend() { _preventPrepend = true; return this; } // =========== Implementation private boolean _modified = true; // Start modified. private boolean _preventPrepend; private final String _fromApp; private final String _fromInit; private String _prefix; private String _reason; private String _batchId; private LinkedHashMap _keyValues; private String _calcualatedString; protected TraceId(String fromApp, String fromInit, String reason) { _fromApp = fromApp; _fromInit = fromInit; _reason = reason; } protected TraceId concat(String prefix) { _prefix = prefix + "+"; return this; } protected TraceId fork(String prefix) { _prefix = prefix + "|"; return this; } @Override public String toString() { // ?: Has it been modified? (Always true at construction) if (!_modified) { // No, not modified - so return what we already have. return _calcualatedString; } // E-> Yes, it is modified. // "Calculate" the resulting TraceId. StringBuilder buf = new StringBuilder(); if (_preventPrepend) { buf.append('!'); } if (_prefix != null) { buf.append(_prefix); } if (_fromApp != null) { buf.append(_fromApp); } if ((_fromApp != null) && (_fromInit != null)) { buf.append("."); } if (_fromInit != null) { buf.append(_fromInit); } if (((_fromApp != null) || (_fromInit != null)) && (_reason != null)) { buf.append(":"); } if (_reason != null) { buf.append(_reason); } if (_batchId != null) { buf.append("[batch=").append(_batchId).append(']'); } // ?: Do we have key-values? if (_keyValues != null) { // -> Yes, there are key-values, so add them. for (Entry entry : _keyValues.entrySet()) { buf.append('[').append(entry.getKey()).append('=').append(entry.getValue()).append(']'); } } else { // -> No key-values, so add an underscore before random part. buf.append('_'); } buf.append(RandomString.partTraceId()); _calcualatedString = buf.toString(); _modified = false; return _calcualatedString; } // CharSequence @Override public int length() { return toString().length(); } @Override public char charAt(int index) { return toString().charAt(index); } @Override public String subSequence(int start, int end) { return toString().substring(start, end); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy