Manual LogEvent emission
Overview
Most code should emit events through LogContext.log_event() or its convenience methods:
ctx.log_info_event(
event="worker.started",
payload={"worker_id": "w1"},
)
That path builds metadata, checks event policy, normalizes the payload, creates LogEvent, and delivers it through the effective sink.
Manual LogEvent emission is the lower-level alternative.
It uses:
ctx.emit_log_event(log_event)
This method expects a fully prepared LogEvent.
It does not apply event policy and does not normalize payload data.
Use it only when the caller has already made those decisions.
Normal path vs manual path
The normal context path is:
ctx.log_event(...)
|
v
build LogEventMeta
|
v
check event policy
|
v
normalize payload
|
v
build LogEvent
|
v
emit through sink
The manual path is:
caller builds LogEvent
|
v
ctx.emit_log_event(log_event)
|
v
emit through sink
Manual emission skips the earlier steps because the caller has already prepared the event.
When to use manual emission
Use manual LogEvent emission when an external or higher-level component already owns event construction.
Good cases:
bridging events from another logging pipeline
replaying stored LogEvent objects
forwarding events between contexts
writing tests for sink behavior
building a specialized log component above LogContext
In these cases, LogContext is used as a delivery boundary, not as the event builder.
When not to use manual emission
Do not use emit_log_event() just to avoid typing a few arguments to log_event().
Avoid it when the caller still needs:
event policy checks
payload normalization
standard metadata construction
context namespace defaulting
level convenience methods
For ordinary manual logging, use:
log_debug_event()
log_info_event()
log_warning_event()
log_error_event()
log_event()
Those methods keep the normal pipeline intact.
Building a LogEvent manually
Manual emission requires creating both LogEventMeta and LogEvent.
import time
from mvx.common.logger import LogEvent, LogEventMeta, LogLevel
meta = LogEventMeta(
event_namespace="my.component",
event_name="external.event",
entity_id="entity-1",
source_path=None,
source_line=None,
source_func=None,
)
log_event = LogEvent(
level=LogLevel.INFO,
meta=meta,
event_outcome=None,
timestamp=time.time(),
payload={
"status": "ready",
},
)
ctx.emit_log_event(log_event)
The payload is used as-is.
No payload processor is applied by emit_log_event().
Payload responsibility
When using emit_log_event(), the caller is responsible for providing a log-ready payload.
That means:
keys are suitable for structured logging
values are already normalized or intentionally raw
payload size is acceptable
sensitive data has already been handled
If you still want the context payload processor to normalize the payload, do it explicitly before building the event:
payload = ctx.normalize_payload(
{
"raw_object": obj,
}
)
Then use the normalized payload in LogEvent.
Event policy responsibility
emit_log_event() does not call is_event_enabled().
If manual emission should still respect event policy, the caller must check it explicitly.
meta = LogEventMeta(
event_namespace="my.component",
event_name="external.event",
entity_id=None,
source_path=None,
source_line=None,
source_func=None,
)
if ctx.is_event_enabled(meta):
ctx.emit_log_event(
LogEvent(
level=LogLevel.INFO,
meta=meta,
event_outcome=None,
timestamp=time.time(),
payload=payload,
)
)
This explicit check is useful when a custom component wants full control over event construction while still using context policy.
Forwarding events
Manual emission can forward an existing LogEvent through another context.
other_ctx.emit_log_event(log_event)
This does not rebuild metadata.
It does not re-run policy.
It does not re-normalize payload.
The event is delivered as it already exists.
This can be useful for bridging or replaying events, but it should be used deliberately.
Testing sinks
Manual emission is useful in sink tests.
A test can build a small LogEvent and send it through a context or directly to a sink.
event = LogEvent(
level=LogLevel.INFO,
meta=LogEventMeta(
event_namespace="test",
event_name="sink.test",
entity_id=None,
source_path=None,
source_line=None,
source_func=None,
),
event_outcome=None,
timestamp=0.0,
payload={"value": 1},
)
ctx.emit_log_event(event)
This avoids coupling the test to payload normalization or event-policy behavior when the test is about delivery.
Error handling boundary
emit_log_event() still uses the context’s effective sink and logging error handling policy.
If the sink raises during delivery, LogContext applies LogErrorHandlingPolicy.
IGNORE
suppress delivery failure
PRINT_STDERR
report through the last-resort stderr path
RAISE
raise LogContextUnableToLog
So manual emission bypasses event building, policy, and normalization, but it does not bypass the context delivery error boundary.
Relationship to log_invocation
log_invocation internally builds prepared LogEvent records for operation outcomes and emits them through the resolved context.
That is similar to manual emission in one sense: the component constructs the event itself.
The difference is that log_invocation owns a specific higher-level behavior:
operation lifecycle -> invoke/success/failed/cancelled outcomes
Manual emission is lower-level and generic.
Use log_invocation for public API operation lifecycle logging.
Use manual emit_log_event() only when you are building another component or bridge that already owns event construction.
Common mistakes
Avoid these mistakes:
calling emit_log_event() with raw application objects in payload
expecting event policy to run automatically
expecting payload normalization to run automatically
forwarding events without considering duplicate delivery
using manual emission for ordinary application logging
The method is powerful because it skips normal event construction steps.
That is also what makes it easy to misuse.
Design summary
emit_log_event() is the low-level delivery boundary of LogContext.
It accepts a prepared LogEvent and sends it through the effective sink.
It does not apply event policy.
It does not normalize payload data.
It still uses the context’s sink delivery and logging infrastructure error handling behavior.
Use it for bridges, replays, tests, and custom logging components that already own event construction.