Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add json logging example #106

Merged
merged 6 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/acceptance-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: grafana/oats
ref: d07befda3cfa162865d8081aa64501ea26578870
ref: 214c70a70ed78ef273db52dc75555536476ce15f
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path: oats
- name: Set up Go
uses: actions/setup-go@v5
Expand Down
3 changes: 1 addition & 2 deletions docker/run-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ wait_ready "Loki" "http://localhost:3100/ready"
wait_ready "Prometheus" "http://localhost:9090/api/v1/status/runtimeinfo"
wait_ready "Tempo" "http://localhost:3200/ready"

# collector may not have a prometheus endpoint exposed if the config has been replaced,
# so we query the otelcol_process_uptime_total metric instead, which checks if the collector is up,
# we query the otelcol_process_uptime_total metric instead, which checks if the collector is up,
# and indirectly checks if the prometheus endpoint is up.
while ! curl -sg 'http://localhost:9090/api/v1/query?query=otelcol_process_uptime_total{}' | jq -r .data.result[0].value[1] | grep '[0-9]' > /dev/null ; do
echo "Waiting for the OpenTelemetry collector to start up..."
Expand Down
22 changes: 22 additions & 0 deletions examples/java/json-logging-ecs/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM eclipse-temurin:21-jdk AS builder

WORKDIR /usr/src/app/

COPY ./mvnw pom.xml ./
COPY ./.mvn ./.mvn
COPY ./src ./src
COPY json-logging-ecs/logback-spring.xml ./src/main/resources/logback-spring.xml
RUN sed -i '/<dependencies>/a <dependency><groupId>co.elastic.logging</groupId><artifactId>logback-ecs-encoder</artifactId><version>1.6.0</version></dependency>' pom.xml
RUN --mount=type=cache,target=/root/.m2 ./mvnw install -DskipTests

FROM eclipse-temurin:21-jre

WORKDIR /usr/src/app/

COPY --from=builder /usr/src/app/target/rolldice.jar ./app.jar
# we ignore the version (which is from upstream) and use the latest version of the grafana distribution
ADD --chmod=644 https://github.com/grafana/grafana-opentelemetry-java/releases/latest/download/grafana-opentelemetry-java.jar /usr/src/app/opentelemetry-javaagent.jar
ENV JAVA_TOOL_OPTIONS=-javaagent:/usr/src/app/opentelemetry-javaagent.jar

EXPOSE 8080
ENTRYPOINT [ "java", "-jar", "./app.jar" ]
10 changes: 10 additions & 0 deletions examples/java/json-logging-ecs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Exporting Application logs using JSON logging in Kubernetes

## Running the example

1. Build the Docker image using using `build.sh`
2. Deploy the manifest using `kubectl apply -f k8s/` (e.g. using [k3d.sh](k3d.sh))
3. Generate traffic using [generate-traffic.sh](../../../generate-traffic.sh)
4. Log in to [http://localhost:3000](http://localhost:3000) with user _admin_ and password _admin_.
5. Go to "Explore"
6. Select "Loki" as data source
5 changes: 5 additions & 0 deletions examples/java/json-logging-ecs/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

set -euo pipefail

docker build -f Dockerfile -t "dice:1.1-SNAPSHOT" ..
15 changes: 15 additions & 0 deletions examples/java/json-logging-ecs/k3d.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash

set -euo pipefail

./build.sh
k3d cluster create jsonlogging || k3d cluster start jsonlogging
k3d image import -c jsonlogging dice:1.1-SNAPSHOT

kubectl apply -f k8s/

kubectl wait --for=condition=ready pod -l app=dice
kubectl wait --for=condition=ready --timeout=5m pod -l app=lgtm

kubectl port-forward service/dice 8080:8080 &
kubectl port-forward service/lgtm 3000:3000 &
172 changes: 172 additions & 0 deletions examples/java/json-logging-ecs/k8s/collector-configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: otel-collector-config
data:
otel-collector-config.yaml: |-
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
prometheus/collector: # needed if you use the docker-lgtm image
config:
scrape_configs:
- job_name: 'opentelemetry-collector'
static_configs:
- targets: [ 'localhost:8888' ]
filelog/json-ecs:
include:
- /var/log/pods/*/dice/*.log
include_file_path: true
operators:
- id: container-parser
type: container
- id: router
type: router
routes:
- output: json_parser
expr: 'body matches "\\{[^{}]*\\}" == true'
- id: json_parser
type: json_parser
on_error: drop # TODO use 'drop_quiet' once available:
body: attributes.message
timestamp:
parse_from: attributes["@timestamp"]
layout: '%Y-%m-%dT%H:%M:%S.%LZ'
severity:
parse_from: attributes["log.level"]
trace:
trace_id:
parse_from: attributes.trace_id
span_id:
parse_from: attributes.span_id
scope_name:
parse_from: attributes["log.logger"]
- id: move_service_namespace
type: move
if: 'attributes["service.namespace"] != nil'
from: attributes["service.namespace"]
to: resource["service.namespace"]
- id: move_service_name
type: move
from: attributes["service.name"]
to: resource["service.name"]
- id: move_service_instance_id
type: move
if: 'attributes["service.instance.id"] != nil'
from: attributes["service.instance.id"]
to: resource["service.instance.id"]
- id: move_deployment_environment
type: move
if: 'attributes["deployment.environment"] != nil'
from: attributes["deployment.environment"]
to: resource["deployment.environment"]
- id: move_thread_name
type: move
from: attributes["process.thread.name"]
to: attributes["thread.name"]
- id: move_error_message
type: move
if: 'attributes["error.message"] != nil'
from: attributes["error.message"]
to: attributes["exception.message"]
- id: move_error_type
type: move
if: 'attributes["error.type"] != nil'
from: attributes["error.type"]
to: attributes["exception.type"]
- id: move_throwable_stacktrace
type: move
if: 'len(attributes["error.stack_trace"]) > 0'
from: attributes["error.stack_trace"]
to: attributes["exception.stacktrace"]
- id: remove_logger_name
type: remove
field: attributes["log.logger"]
- id: remove_timestamp
type: remove
field: attributes["@timestamp"]
- id: remove_level
type: remove
field: attributes["log.level"]
- id: remove_span_id
if: 'attributes["span_id"] != nil'
type: remove
field: attributes.span_id
- id: remove_trace_id
if: 'attributes["trace_id"] != nil'
type: remove
field: attributes.trace_id
- id: remove_message
type: remove
field: attributes.message
- id: remove_ecs_version
type: remove
field: attributes["ecs.version"]
- id: remove_ecs_event_dataset
type: remove
field: attributes["event.dataset"]
- id: remove_trace_flags
type: remove
field: attributes["trace_flags"]
- id: remove_logtag
type: remove
field: attributes.logtag
- id: remove_file
type: remove
field: attributes["log.file.path"]
- id: remove_filename
type: remove
field: attributes["log.file.name"]
- id: remove_stream
type: remove
field: attributes["log.iostream"]
- id: remove_time
type: remove
field: attributes.time

processors:
batch:
resourcedetection:
detectors: [ "env", "system" ]
override: false

exporters:
otlphttp/metrics:
endpoint: http://localhost:9090/api/v1/otlp
otlphttp/traces:
endpoint: http://localhost:4418
otlphttp/logs:
endpoint: http://localhost:3100/otlp
debug/metrics:
verbosity: detailed
debug/traces:
verbosity: detailed
debug/logs:
verbosity: detailed
nop:

service:
pipelines:
traces:
receivers: [ otlp ]
processors: [ batch ]
exporters: [ otlphttp/traces ]
metrics:
receivers: [ otlp, prometheus/collector ]
processors: [ batch ]
exporters: [ otlphttp/metrics ]
logs/otlp:
receivers: [ otlp ]
processors: [ batch ]
exporters: [ otlphttp/logs ]
logs/json-ecs:
receivers: [ filelog/json-ecs ]
processors: [ batch ]
exporters: [ otlphttp/logs ]
# exporters: [ otlphttp/logs, debug/logs ] # Uncomment this line to enable debug logging


44 changes: 44 additions & 0 deletions examples/java/json-logging-ecs/k8s/dice.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
apiVersion: v1
kind: Service
metadata:
name: dice
spec:
selector:
app: dice
ports:
- protocol: TCP
port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dice
spec:
replicas: 1
selector:
matchLabels:
app: dice
template:
metadata:
labels:
app: dice
spec:
containers:
- name: dice
image: dice:1.1-SNAPSHOT
imagePullPolicy: Never
ports:
- containerPort: 8080
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://lgtm:4318"
- name: OTEL_LOGS_EXPORTER
value: "none" # to avoid duplicate logs
- name: OTEL_RESOURCE_ATTRIBUTES
value: service.name=dice,service.namespace=shop,service.version=1.1,deployment.environment=staging
- name: OTEL_INSTRUMENTATION_COMMON_MDC_RESOURCE_ATTRIBUTES
value: "service.namespace,service.instance.id,deployment.environment"
- name: SERVICE_NAME
value: dice

86 changes: 86 additions & 0 deletions examples/java/json-logging-ecs/k8s/lgtm.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
apiVersion: v1
kind: Service
metadata:
name: lgtm
spec:
selector:
app: lgtm
ports:
- name: grafana
protocol: TCP
port: 3000
targetPort: 3000
- name: otel-grpc
protocol: TCP
port: 4317
targetPort: 4317
- name: otel-http
protocol: TCP
port: 4318
targetPort: 4318
- name: prometheus # needed for automated tests
protocol: TCP
port: 9090
targetPort: 9090
- name: loki # needed for automated tests
protocol: TCP
port: 3100
targetPort: 3100
- name: tempo # needed for automated tests
protocol: TCP
port: 3200
targetPort: 3200
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: lgtm
spec:
replicas: 1
selector:
matchLabels:
app: lgtm
template:
metadata:
labels:
app: lgtm
spec:
containers:
- name: lgtm
image: grafana/otel-lgtm:latest
ports:
- containerPort: 3000
- containerPort: 4317
- containerPort: 4318
- containerPort: 9090 # needed for automated tests
- containerPort: 3100 # needed for automated tests
- containerPort: 3200 # needed for automated tests
readinessProbe:
exec:
command:
- cat
- /tmp/ready
volumeMounts:
- mountPath: /otel-lgtm/otelcol-config.yaml
name: otel-collector-config
subPath: otel-collector-config.yaml
readOnly: true
- mountPath: /var/log
name: varlog
readOnly: true
- mountPath: /var/lib/docker/containers
name: varlibdockercontainers
readOnly: true
env:
- name: ENABLE_LOGS_OTELCOL
value: "true"
volumes:
- name: otel-collector-config
configMap:
name: otel-collector-config
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
7 changes: 7 additions & 0 deletions examples/java/json-logging-ecs/logback-spring.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<configuration>
<include resource="co/elastic/logging/logback/boot/ecs-console-appender.xml" />

<root level="info">
<appender-ref ref="ECS_JSON_CONSOLE"/>
</root>
</configuration>
Loading
Loading