CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches
Log Context

Macros

#define withLogCtx(...)
 
#define withLogLocal()   blkWrap(_logLocalPush(), _logLocalPop())
 

Functions

void logCtxPop (void)
 
LogCtxlogCtxCurrent (void)
 
LogCtxlogCtxAcquire (LogCtx *ctx)
 
void logCtxRelease (LogCtx **ctx)
 
LogCtxlogCtxSwap (LogCtx *ctx)
 
void logCtxRestore (LogCtx *ctx)
 
const LogCtxlogCtxParent (const LogCtx *ctx)
 
uint32 logCtxNumVars (const LogCtx *ctx)
 
const stvarlogCtxVars (const LogCtx *ctx)
 
bool logCtxShadowed (const LogCtx *top, const LogCtx *node, uint32 idx, const char *key)
 

Detailed Description

Correlation fields attached to everything logged on a thread, without threading a context object through every function that might log:

withLogCtx (stvark(reqid, suid, id), stvark(tenant, string, tenant)) {
handleRequest(req); // every record logged in here carries reqid and tenant
}
#define withLogCtx(...)
Definition logctx.h:72
#define stvark(key, typen, val)
Definition stvar.h:204

Contexts are immutable and shared. Entering one allocates a single node pointing at the enclosing context; a record snapshots the whole chain by taking one reference. Nothing is copied per record, and a record that outlives the block it was logged in still renders the fields that were in scope.

Fields are keyed variants, the same ones logFmt() takes (see stvark()). Structured destinations emit all of them; text destinations render a configured subset, or none, so a console does not fill up with correlation ids.

A message template can name them too, under the same keys, so a correlation id can appear in the sentence rather than only in the annotation a destination appends:

withLogCtx (stvark(reqid, string, id)) {
logFmt(Info, _SL("handling ${string:reqid}"), stvNone);
}
#define logFmt(level, fmt,...)
Definition log.h:643
#define _SL(s)
Inline ASCII string literal with compile-time embedded length (STR_LEN8). Content must be < 200 bytes...
Definition strliteral.h:207
#define stvNone
Definition stvar.h:178

This costs the call site nothing it was not already paying: a keyed variant is invisible to an unkeyed placeholder, so context fields cannot renumber or otherwise disturb the arguments the call site wrote. An argument sharing a key with a context field wins, being the more specific of the two. A key that is in no context in scope leaves the placeholder unmatched, which fails the format like any other unmatched placeholder unless it carries a ;default.

Only logFmt() messages are templates. A logStr() message is literal, so one containing ${...} is delivered unchanged whether or not a context is in scope.

Nesting shadows rather than merges. An inner context that reuses a key wins; the outer value is still there but is not emitted twice.

Work that hops threads keeps its context. A task inherits the context of whoever submitted it, restored for the duration of its run and undone afterwards. Without that, correlation is lost the moment work is handed to a queue, which in an async server is immediately.

Macro Definition Documentation

◆ withLogCtx

#define withLogCtx (   ...)
Value:
blkWrap (_logCtxPush(count_macro_args(__VA_ARGS__), \
(stvar[]) { __VA_ARGS__ }), \
void logCtxPop(void)
#define blkWrap(before,...)
Definition block.h:230
#define stvar(typen, val)
Definition stvar.h:162
#define count_macro_args(...)
Definition args.h:81

void withLogCtx(...)

Runs the following block with additional fields attached to everything logged on this thread

The context is popped when the block exits, including via break or continue. return is not allowed inside the block, as with every block-wrapping macro in cx.

Parameters
...Keyed variants, e.g. stvark(reqid, suid, id)
withLogCtx (stvark(reqid, suid, id)) {
logStr(Info, _SL("started")); // carries reqid
}
#define logStr(level, str)
Definition log.h:612

Definition at line 72 of file logctx.h.

◆ withLogLocal

#define withLogLocal ( )    blkWrap(_logLocalPush(), _logLocalPop())

void withLogLocal(void)

Runs the following block with everything logged on this thread kept on this machine

Records logged inside the block still reach every local destination, so whatever is running in there stays diagnosable; they simply never reach a destination that sends them elsewhere. Use it around the code that carries log traffic off the machine, so that a transport which logs about its own sends cannot feed itself.

The scope is popped when the block exits, including via break or continue. return is not allowed inside the block, as with every block-wrapping macro in cx.

sendToCollector(buf, len);
}
#define withLogLocal()
Definition logctx.h:97

Definition at line 97 of file logctx.h.

Function Documentation

◆ logCtxAcquire()

LogCtx * logCtxAcquire ( LogCtx ctx)

Take a reference to a context

Parameters
ctxContext to acquire, or NULL
Returns
The same context, or NULL

◆ logCtxCurrent()

LogCtx * logCtxCurrent ( void  )

The calling thread's current context, or NULL if it has none

Borrowed: valid until the thread leaves the context. Acquire it to keep it.

Returns
Current context, or NULL

◆ logCtxNumVars()

uint32 logCtxNumVars ( const LogCtx ctx)

How many fields this context node adds

Does not include fields inherited from logCtxParent(); walk the chain for the full set.

Parameters
ctxContext to inspect
Returns
Number of fields

◆ logCtxParent()

const LogCtx * logCtxParent ( const LogCtx ctx)

The context this one was entered from, or NULL at the outermost

Parameters
ctxContext to inspect
Returns
Enclosing context, or NULL

◆ logCtxPop()

void logCtxPop ( void  )

Leave the innermost context on this thread

Only needed when the enter/leave pair cannot be a block; prefer withLogCtx().

◆ logCtxRelease()

void logCtxRelease ( LogCtx **  ctx)

Release a reference to a context

Parameters
ctxContext to release; set to NULL

◆ logCtxRestore()

void logCtxRestore ( LogCtx ctx)

Put back a context taken by logCtxSwap()

Consumes the reference logCtxSwap() handed out.

Parameters
ctxContext returned by logCtxSwap(), or NULL

◆ logCtxShadowed()

bool logCtxShadowed ( const LogCtx top,
const LogCtx node,
uint32  idx,
const char *  key 
)

Has this key already been seen by a walk in progress?

Nesting shadows: an inner context that reuses a key wins, and the outer one should not be emitted a second time. A destination walking the chain from top calls this for each candidate field to find out whether a more deeply nested context already supplied it.

Parameters
topContext the walk started at (the record's context)
nodeContext node the candidate field belongs to
idxIndex of the candidate field within that node
keyThe candidate field's key
Returns
true if the key already appeared, so this field should be skipped
for (const LogCtx *c = rec->ctx; c; c = logCtxParent(c)) {
const stvar *vars = logCtxVars(c);
for (uint32 i = 0; i < logCtxNumVars(c); i++) {
const char *key = stvarKey(&vars[i]);
if (!key || logCtxShadowed(rec->ctx, c, i, key))
continue;
emitField(key, &vars[i]);
}
}
struct LogCtx LogCtx
Opaque handle to a log context; see logctx.h.
Definition log.h:227
uint32 logCtxNumVars(const LogCtx *ctx)
const LogCtx * logCtxParent(const LogCtx *ctx)
const stvar * logCtxVars(const LogCtx *ctx)
bool logCtxShadowed(const LogCtx *top, const LogCtx *node, uint32 idx, const char *key)
const char * stvarKey(const stvar *v)
Definition stype.h:514

◆ logCtxSwap()

LogCtx * logCtxSwap ( LogCtx ctx)

Replace this thread's context wholesale

For carrying a context across a thread boundary: capture logCtxCurrent() where the work is submitted, then swap it in where the work runs. Ownership of the returned context transfers to the caller, who must hand it to logCtxRestore().

Parameters
ctxContext to install, or NULL for none
Returns
The context that was installed before, or NULL
LogCtx *prev = logCtxSwap(task->logctx);
runTask(task);
void logCtxRestore(LogCtx *ctx)
LogCtx * logCtxSwap(LogCtx *ctx)

◆ logCtxVars()

const stvar * logCtxVars ( const LogCtx ctx)

This context node's fields

Parameters
ctxContext to inspect
Returns
Array of logCtxNumVars() keyed variants
for (const LogCtx *c = rec->ctx; c; c = logCtxParent(c)) {
const stvar *vars = logCtxVars(c);
for (uint32 i = 0; i < logCtxNumVars(c); i++)
emitField(stvarKey(&vars[i]), &vars[i]);
}