Option 1: OpenTelemetry Java agent (no code changes)
The agent captures Logback and Log4j2 records with MDC and exception details and exports them over OTLP. Attach it with a JVM flag and set the environment.
curl -sSL -o opentelemetry-javaagent.jar \
https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
export OTEL_SERVICE_NAME=orders
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod
export OTEL_LOGS_EXPORTER=otlp
export OTEL_TRACES_EXPORTER=none
export OTEL_METRICS_EXPORTER=none
export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://logs.uptimeeye.com/v1/ingest/otlp/v1/logs
export OTEL_EXPORTER_OTLP_LOGS_HEADERS="Authorization=Bearer%20ue_ingest_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
java -javaagent:./opentelemetry-javaagent.jar -jar orders.jarservice.name:=orders severity_text:=ERROR. MDC keys arrive as attributes; exception.stacktrace holds the full trace.Option 2: Spring Boot structured logging + agent (containers)
Spring Boot 3.4+ can write ECS/JSON to stdout with one property. Your Kubernetes or Docker agent then lifts the JSON keys to fields.
logging.structured.format.console=ecs
logging.structured.ecs.service.name=orders
logging.structured.ecs.service.environment=prodOption 3: Logback JSON to a file, tailed by Fluent Bit or Vector
For VMs. logstash-logback-encoder writes one JSON object per line; the agent guides on this site pick it up with _msg_field=message&_time_field=@timestamp.
<configuration>
<appender name="JSON" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/var/log/orders/application.json</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/var/log/orders/application.%d{yyyy-MM-dd}.json</fileNamePattern>
<maxHistory>3</maxHistory>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"service":"orders","env":"prod"}</customFields>
<includeMdcKeyName>traceId</includeMdcKeyName>
<includeMdcKeyName>orderId</includeMdcKeyName>
</encoder>
</appender>
<root level="INFO"><appender-ref ref="JSON"/></root>
</configuration>Option 4: the plain-text file, right now
Keep your pattern layout and upload the file. Timestamps and levels are detected, stack traces stay with their line.
curl -sS -X POST "https://logs.uptimeeye.com/v1/ingest/text?service=orders&env=prod&_stream_fields=service,env" \
-H "Authorization: Bearer ue_ingest_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" -H "Content-Type: text/plain" \
--data-binary @/var/log/orders/application.logFields you get
These show up in the fields panel and can be used in every filter:
service/service.name,envlevel/severity_textlogger_name,thread_name- MDC keys such as
traceId,orderId stack_trace/exception.stacktrace
Tips
- Log in UTC or with an explicit offset — the plain-text endpoint reads zone-less timestamps as UTC.
- Put business identifiers into MDC (
MDC.put("orderId", id)) instead of the message; they become filterable fields in every option above. - Log4j2 users: the OpenTelemetry agent covers Log4j2 too; for files use
JsonTemplateLayoutwith theEcsLayout.jsontemplate.
FAQ
- Which option should I pick?
- Kubernetes or Docker with a log agent already in place → Option 2. VM without an agent → Option 1 (nothing to install besides the jar) or Option 3. Just trying it out → Option 4.
- Does the OpenTelemetry agent slow the JVM down?
- Startup takes a bit longer (instrumentation), steady state overhead is low. With traces and metrics exporters set to
noneonly log records are exported. - Where do I get the ingest key?
- In the app under API Keys → New API Key → type “Log ingest”. The key starts with
ue_ingest_and is shown once. Management keys (ue_live_) are refused by the ingest endpoint. - How do I check that logs arrive?
- Open Logs in the app, pick the 15m range and search for
service:=<your service>. New lines are searchable within about a second; Live tail shows them with a ~6 s delay.