Instrumentation API Reference¶
Complete reference for the Micromegas Unreal Engine instrumentation API.
Header File¶
All instrumentation macros are available by including:
Logging API¶
MICROMEGAS_LOG¶
Records a log entry with dynamic string content.
Parameters:
target(const char*): Log target/category (e.g., "Game", "Network", "AI")level(MicromegasTracing::LogLevel): Severity levelmessage(FString): The log message
Log Levels:
MicromegasTracing::LogLevel::Fatal- Critical errors causing shutdownMicromegasTracing::LogLevel::Error- Errors requiring attentionMicromegasTracing::LogLevel::Warn- Warning conditionsMicromegasTracing::LogLevel::Info- Informational messagesMicromegasTracing::LogLevel::Debug- Debug informationMicromegasTracing::LogLevel::Trace- Detailed trace information
Example:
MICROMEGAS_LOG("Game", MicromegasTracing::LogLevel::Info,
TEXT("Player connected"));
MICROMEGAS_LOG("Network", MicromegasTracing::LogLevel::Error,
FString::Printf(TEXT("Connection failed: %s"), *ErrorMessage));
MICROMEGAS_LOG_PROPERTIES¶
Records a log entry with additional structured properties.
Parameters:
target(const char*): Log target/categorylevel(MicromegasTracing::LogLevel): Severity levelproperties(PropertySet*): Additional key-value propertiesmessage(FString): The log message
Example:
PropertySet* Props = CreatePropertySet();
Props->Add("player_id", "12345");
Props->Add("action", "login");
MICROMEGAS_LOG_PROPERTIES("Game", MicromegasTracing::LogLevel::Info,
Props, TEXT("Player action recorded"));
UE_LOG Integration¶
All existing UE_LOG statements are automatically captured by Micromegas when the log interop is initialized. No code changes required.
// These are automatically sent to telemetry
UE_LOG(LogTemp, Warning, TEXT("This is captured by Micromegas"));
UE_LOG(LogGameMode, Error, TEXT("So is this"));
Metrics API¶
MICROMEGAS_IMETRIC¶
Records an integer metric value.
Parameters:
target(const char*): Metric target/categorylevel(MicromegasTracing::Verbosity): Verbosity levelname(const TCHAR*): Metric nameunit(const TCHAR*): Unit of measurementexpression(int64): Value or expression to record
Verbosity Levels:
MicromegasTracing::Verbosity::Low- Critical metrics onlyMicromegasTracing::Verbosity::Med- Standard metricsMicromegasTracing::Verbosity::High- Detailed metrics
Common Units:
TEXT("count")- Simple counterTEXT("bytes")- Memory/data sizeTEXT("ms")- MillisecondsTEXT("percent")- Percentage (0-100)TEXT("ticks")- Will be automatically converted into nanoseconds
Example:
MICROMEGAS_IMETRIC("Game", MicromegasTracing::Verbosity::Med,
TEXT("PlayerCount"), TEXT("count"),
GetWorld()->GetNumPlayerControllers());
MICROMEGAS_IMETRIC("Memory", MicromegasTracing::Verbosity::Low,
TEXT("TextureMemory"), TEXT("bytes"),
GetTextureMemoryUsage());
MICROMEGAS_FMETRIC¶
Records a floating-point metric value.
Parameters:
target(const char*): Metric target/categorylevel(MicromegasTracing::Verbosity): Verbosity levelname(const TCHAR*): Metric nameunit(const TCHAR*): Unit of measurementexpression(double): Value or expression to record
Example:
MICROMEGAS_FMETRIC("Performance", MicromegasTracing::Verbosity::Med,
TEXT("FrameTime"), TEXT("ms"),
DeltaTime * 1000.0);
MICROMEGAS_FMETRIC("Game", MicromegasTracing::Verbosity::High,
TEXT("HealthPercent"), TEXT("percent"),
(Health / MaxHealth) * 100.0);
Spans/Tracing API¶
Important: Spans are disabled by default. Enable with console command: telemetry.spans.enable 1. Use reasonable sampling strategy for high-frequency spans.
MICROMEGAS_SPAN_FUNCTION¶
Traces the current function's execution time using the function name as the span name.
Parameters:
target(const char*): Span target/category
Example:
void AMyActor::ComplexCalculation()
{
MICROMEGAS_SPAN_FUNCTION("Game.Physics");
// Function is automatically traced
// ... complex physics calculations ...
}
MICROMEGAS_SPAN_SCOPE¶
Creates a named scope span with a static name.
Parameters:
target(const char*): Span target/categoryname(const char*): Static span name
Example:
void ProcessAI()
{
{
MICROMEGAS_SPAN_SCOPE("AI", "Pathfinding");
// ... pathfinding code ...
}
{
MICROMEGAS_SPAN_SCOPE("AI", "DecisionTree");
// ... decision tree evaluation ...
}
}
MICROMEGAS_SPAN_NAME¶
Creates a span with a dynamic name (must be statically allocated).
Parameters:
target(const char*): Span target/categoryname_expression: Expression returning a statically allocated string (e.g., FName)
Example:
void ProcessAsset(const FString& AssetPath)
{
FName AssetName(*AssetPath);
MICROMEGAS_SPAN_NAME("Content", AssetName);
// ... process asset ...
}
MICROMEGAS_SPAN_UOBJECT¶
Creates a span named after a UObject.
Parameters:
target(const char*): Span target/categoryobject(UObject*): The UObject whose name to use
Example:
void AMyActor::Tick(float DeltaTime)
{
MICROMEGAS_SPAN_UOBJECT("Game.Actors", this);
Super::Tick(DeltaTime);
// ... tick logic ...
}
MICROMEGAS_SPAN_NAME_CONDITIONAL¶
Creates a span conditionally.
Parameters:
target(const char*): Span target/categorycondition(bool): Whether to create the spanname: Span name if condition is true
Example:
void RenderFrame(bool bDetailedProfiling)
{
MICROMEGAS_SPAN_NAME_CONDITIONAL("Render", bDetailedProfiling,
TEXT("DetailedFrame"));
// ... rendering code ...
}
Network Tracing¶
Audience
This section is written to be readable by both human integrators and coding-agent LLMs (e.g. Claude Code). Each macro entry follows a fixed shape — signature, parameters, semantics, bit-source expression, example — so an agent can apply each site without ambiguity.
For the engine-side recipe — which UE files to modify and where — see Network Tracing.
Net-trace macros capture per-connection replication traffic with bit-size attribution. They live in the same header as logs/metrics/spans:
All macros are RAII where applicable; early returns close scopes automatically. When MICROMEGAS_NET_TRACE_ENABLED is 0, every macro expands to nothing — zero overhead.
Bit-source cheat sheet¶
Every scope macro that takes a getBitsExpr parameter captures the position at entry and measures the delta at exit. The expression must refer to a bit stream that's being written to (send) or read from (receive) inside the scope.
| Situation | Expression |
|---|---|
| Classic send (outgoing bunch) | Bunch.GetNumBits() |
| Classic receive (incoming reader) | Reader.GetPosBits() |
Classic RPC send (TempWriter) |
TempWriter.GetNumBits() |
| Classic fast-array property writer | TempBitWriter.GetNumBits() |
| Iris send | Context.GetBitStreamWriter()->GetPosBits() |
| Iris receive | Context.GetBitStreamReader()->GetPosBits() |
Flat property calls (MICROMEGAS_NET_PROPERTY) pass the pre-computed bit size directly — no bit stream expression needed.
MICROMEGAS_NET_CONNECTION_SCOPE¶
Opens a per-connection scope. All object/property/RPC events emitted inside are attributed to this connection.
Parameters:
ConnectionName(FName): stable connection identifier (useConnection->GetPlayerOnlinePlatformName()withGetFName()fallback)bIsOutgoing(bool):truefor send paths,falsefor receive paths
Semantics:
- RAII; closes on scope exit
- Connection scopes do not nest — only the outermost emits, inner ones are absorbed as no-ops and logged once via
LogMicromegasNet - Snapshots the current runtime verbosity at the outermost Begin; CVar changes take effect at the next outer scope
Emits: NetConnectionBeginEvent on entry, NetConnectionEndEvent (with bit_size = sum of root object/RPC bits) on exit.
Example:
void UNetConnection::ReceivedPacket(FBitReader& Reader)
{
FName MmConnectionName = GetPlayerOnlinePlatformName();
if (MmConnectionName == NAME_None) { MmConnectionName = GetFName(); }
MICROMEGAS_NET_CONNECTION_SCOPE(MmConnectionName, /*bIsOutgoing=*/ false);
// ... packet processing ...
}
MICROMEGAS_NET_OBJECT_SCOPE¶
Opens a per-object scope (root actor or subobject). Measures bit-stream delta from entry to exit.
Parameters:
ObjectName:FNameorconst TCHAR*(anything acceptable toStaticStringRef)getBitsExpr: expression returning the current bit-stream position (see cheat sheet above)
Semantics:
- RAII; on destruction emits
NetObjectEndEventwithbit_size = GetBits() - StartBits - Depth 0 (root) requires verbosity ≥
RootObjects; depth 1+ requires ≥Objects - Classic emits subobjects as peers at depth 0; Iris emits them nested at depth 1+
Emits: NetObjectBeginEvent on entry, NetObjectEndEvent on exit.
Example (classic send):
Example (Iris send, with null guard):
MICROMEGAS_NET_OBJECT_SCOPE(
(ObjectData.Protocol && ObjectData.Protocol->DebugName) ? ObjectData.Protocol->DebugName->Name : TEXT("Unknown"),
Context.GetBitStreamWriter()->GetPosBits());
MICROMEGAS_NET_RPC_SCOPE¶
Opens a per-RPC scope. Same shape as object scope but emits NetRPCBeginEvent / NetRPCEndEvent.
Parameters:
FunctionName:FNameorconst TCHAR*getBitsExpr: expression returning the current bit-stream position
Semantics:
- RAII;
EndRPCapplies anObjectDepth == 0gate that prevents double-counting when an RPC fires inside an object scope (nested RPC bits roll into the outer object, not double-attributed to the connection)
Example (classic send):
Example (Iris receive, post-resolve):
MICROMEGAS_NET_RPC_SCOPE(BlobDescriptor->DebugName->Name,
Context.GetBitStreamReader()->GetPosBits());
MICROMEGAS_NET_PROPERTY¶
Flat property leaf — emits a single NetPropertyEvent with the pre-computed bit size. No Begin/End pair.
Parameters:
PropertyName:FNameorconst TCHAR*bitSize(uint32): pre-computed bit length
Semantics:
- Gated at verbosity ≥
Properties - Use when the bit size is already known (e.g.
SharedPropInfo->PropBitLength, or aNumEndBits - NumStartBitsdelta captured forNETWORK_PROFILER)
Example:
MICROMEGAS_NET_PROPERTY_SCOPE¶
Scope-form property — measures the bit-stream delta across the wrapped serialize/deserialize call and emits a single NetPropertyEvent on destruction (still a leaf, no Begin/End pair).
Parameters:
PropertyName:FNameorconst TCHAR*getBitsExpr: bit-stream position expression (see cheat sheet)
Semantics:
- Use when no pre-computed bit size is available (Iris properties, classic receive paths)
- The scope only emits on destruction, after the wrapped serializer call has run
Example (classic receive):
Example (Iris receive):
MICROMEGAS_NET_PROPERTY_SCOPE(MemberDebugDescriptors[MemberIt].DebugName->Name,
Context.GetBitStreamReader()->GetPosBits());
MICROMEGAS_NET_SUSPEND_SCOPE¶
Zeroes out every MICROMEGAS_NET_* call inside its lifetime without touching depth counters. Safe to nest under an active live scope.
Parameters: none.
Semantics:
- RAII;
Dispatch::NetSuspend()on entry,NetResume()on exit - Use for code paths that process packets/bunches but shouldn't contribute to attribution: demo recording, replay scrubbing, server-side simulation
Example:
void UDemoNetDriver::ProcessRemoteFunction(...)
{
MICROMEGAS_NET_SUSPEND_SCOPE();
InternalProcessRemoteFunction(...);
}
Verbosity Levels¶
Runtime verbosity is a 0–4 enum. Depth-based gating inside NetTraceWriter:
| Level | Name | Emits |
|---|---|---|
| 0 | Off |
Nothing |
| 1 | Packets |
Connection scopes only |
| 2 | RootObjects |
+ root object scopes (depth 0) |
| 3 | Objects |
+ nested object scopes (depth 1+) |
| 4 | Properties |
+ per-property leaf events, + RPC scopes |
Default: level 2 (RootObjects) — production setting.
Root RPC bits (ObjectDepth == 0) still contribute to NetConnectionEndEvent.bit_size at every verbosity ≥ Packets, even though NetRPCBeginEvent / NetRPCEndEvent records are only emitted at level 4.
Snapshot invariant: the writer captures EffectiveVerbosity at the outermost BeginConnection and uses that snapshot for every gating decision in the scope. CVar-driven changes take effect at the next outer scope, never mid-scope.
Console & Command Line¶
- CVar:
telemetry.net.verbosity <0-4>— sets runtime verbosity. Effective at the next outer connection scope. - Command-line flag:
-MicromegasNetTrace=N— sets initial verbosity at process start.
Physical packet metrics¶
Two integer metrics are emitted via MICROMEGAS_IMETRIC from the instrumented engine code (see Network Tracing § 3.1, § 3.2):
net.packet_sent_bits(unitbits) —SendBuffer.GetNumBits()inFlushNetnet.packet_received_bits(unitbits) —Reader.GetNumBits()inReceivedPacket
These are wire bits including packet headers, bunch headers, NetGUID exports, control bunches, and voice. Compare against sum(NetConnectionEndEvent.bit_size) for content-vs-wire reconciliation — the gap is framing overhead.
Default Context API¶
The Default Context allows setting global properties that are automatically attached to all telemetry.
Accessing the Default Context¶
Set¶
Adds or updates a context property.
Example:
if (auto* Ctx = MicromegasTracing::Dispatch::GetDefaultContext())
{
Ctx->Set(FName("user_id"), FName(*UserId));
Ctx->Set(FName("session_id"), FName(*SessionId));
Ctx->Set(FName("map"), FName(*GetWorld()->GetMapName()));
}
Unset¶
Removes a context property.
Example:
Clear¶
Removes all context properties.
Example:
Copy¶
Copies current context to a map.
Example:
Console Commands¶
Runtime control commands available in the Unreal console:
telemetry.enable¶
Initializes the telemetry system if not already enabled.
telemetry.flush¶
Forces immediate flush of all pending telemetry events.
telemetry.spans.enable¶
Enables or disables span recording.
telemetry.spans.all¶
Enables recording of all spans without sampling.
Best Practices¶
Performance¶
- Use sampling for high-frequency spans - It's OK to keep spans enabled with reasonable sampling
- Use appropriate verbosity - Lower verbosity for high-frequency metrics
- Batch operations - Let the system batch; avoid frequent flushes
- Static strings - Use TEXT() macro for string literals
- Limit context cardinality - Context keys/values are never freed
Error Handling¶
Always check for null pointers when using the context API:
if (auto* Ctx = MicromegasTracing::Dispatch::GetDefaultContext())
{
// Safe to use Ctx
Ctx->Set(FName("key"), FName("value"));
}
Thread Safety¶
All Micromegas APIs are thread-safe and can be called from any thread:
// Safe from game thread
MICROMEGAS_LOG("Game", MicromegasTracing::LogLevel::Info, TEXT("Game thread"));
// Safe from render thread
MICROMEGAS_LOG("Render", MicromegasTracing::LogLevel::Info, TEXT("Render thread"));
// Safe from worker threads
ParallelFor(NumItems, [](int32 Index)
{
MICROMEGAS_IMETRIC("Worker", MicromegasTracing::Verbosity::High,
TEXT("ItemProcessed"), TEXT("count"), 1);
});
Integration Examples¶
With Gameplay Abilities¶
void UMyGameplayAbility::ActivateAbility(...)
{
MICROMEGAS_SPAN_NAME("Abilities", GetFName());
MICROMEGAS_LOG("Abilities", MicromegasTracing::LogLevel::Info,
FString::Printf(TEXT("Ability %s activated"), *GetName()));
Super::ActivateAbility(...);
}
With Animation¶
void UAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
MICROMEGAS_SPAN_FUNCTION("Animation");
MICROMEGAS_FMETRIC("Animation", MicromegasTracing::Verbosity::High,
TEXT("UpdateTime"), TEXT("ms"), DeltaSeconds * 1000);
Super::NativeUpdateAnimation(DeltaSeconds);
}