Skip to content

Instantly share code, notes, and snippets.

@defanator
Created May 14, 2025 12:55
Show Gist options
  • Select an option

  • Save defanator/cb8788ecc2fc5089fcea53b7fa54839d to your computer and use it in GitHub Desktop.

Select an option

Save defanator/cb8788ecc2fc5089fcea53b7fa54839d to your computer and use it in GitHub Desktop.

Revisions

  1. defanator created this gist May 14, 2025.
    69 changes: 69 additions & 0 deletions compose.yml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,69 @@
    networks:
    grafana:
    name: grafana

    services:
    loki:
    container_name: loki
    image: docker.io/grafana/loki:3.5.0
    command: -config.file=/etc/loki/loki.yml
    volumes:
    - ./loki.yml:/etc/loki/loki.yml
    ports:
    - "3100:3100"
    networks:
    - grafana

    grafana:
    container_name: grafana
    image: docker.io/grafana/grafana-enterprise:12.0.0
    environment:
    - GF_PATHS_PROVISIONING=/etc/grafana/provisioning
    - GF_AUTH_ANONYMOUS_ENABLED=true
    - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
    entrypoint:
    - sh
    - -euc
    - |
    mkdir -p /etc/grafana/provisioning/datasources
    cat <<EOF > /etc/grafana/provisioning/datasources/ds.yaml
    apiVersion: 1
    datasources:
    - name: Prometheus
    type: prometheus
    access: proxy
    orgId: 1
    url: http://prometheus:9090
    basicAuth: false
    isDefault: true
    version: 1
    editable: false
    - name: Loki
    type: loki
    access: proxy
    orgId: 1
    url: http://loki:3100
    basicAuth: false
    isDefault: false
    version: 1
    editable: false
    EOF
    /run.sh
    ports:
    - "3000:3000"
    networks:
    - grafana

    alloy:
    container_name: alloy
    image: docker.io/grafana/alloy:v1.8.2
    environment:
    - HOSTNAME
    command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data --disable-reporting --stability.level=generally-available /etc/alloy/config.alloy
    volumes:
    - ./config.alloy:/etc/alloy/config.alloy
    ports:
    - "12345:12345"
    - "3101:3100"
    networks:
    - grafana
    76 changes: 76 additions & 0 deletions config.alloy
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,76 @@
    logging {
    level = "debug"
    format = "logfmt"
    }

    livedebugging {
    enabled = true
    }

    loki.source.api "nginxagents" {
    http {
    listen_port = 3100
    }

    graceful_shutdown_timeout = "0s"
    forward_to = [otelcol.receiver.loki.loki2otlp.receiver]
    labels = {}
    relabel_rules = null
    use_incoming_timestamp = true
    }

    otelcol.receiver.loki "loki2otlp" {
    output {
    //logs = [otelcol.exporter.loki.otlp2loki.input]
    //logs = [otelcol.processor.filter.otlp_filter.input]
    logs = [otelcol.processor.transform.otlp_transformer.input]
    }
    }

    otelcol.processor.filter "otlp_filter" {
    error_mode = "ignore"

    logs {
    log_record = [
    `UnixSeconds(Now()) - UnixSeconds(log.observed_time) > 5`,
    ]
    }

    output {
    logs = [otelcol.exporter.loki.otlp2loki.input]
    }
    }

    otelcol.processor.transform "otlp_transformer" {
    error_mode = "ignore"

    log_statements {
    context = "log"

    //statements = [
    // `set(attributes["is_stale"], "true") where (UnixSeconds(Now()) - UnixSeconds(log.observed_time) > 5)`,
    //]

    //statements = [
    // `set(attributes["is_stale"], "true") where (UnixSeconds(Now()) - UnixSeconds(observed_timestamp) > 5)`,
    // ]

    statements = [
    `set(attributes["obs_ts"], UnixSeconds(log.observed_time))`,
    ]
    }

    output {
    logs = [otelcol.exporter.loki.otlp2loki.input]
    }
    }

    otelcol.exporter.loki "otlp2loki" {
    forward_to = [loki.write.loki.receiver]
    }

    loki.write "loki" {
    endpoint {
    url = "http://loki:3100/loki/api/v1/push"
    }
    }
    33 changes: 33 additions & 0 deletions loki.yml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,33 @@
    auth_enabled: false

    server:
    http_listen_port: 3100

    common:
    instance_addr: 127.0.0.1
    path_prefix: /loki
    storage:
    filesystem:
    chunks_directory: /loki/chunks
    rules_directory: /loki/rules
    replication_factor: 1
    ring:
    kvstore:
    store: inmemory

    schema_config:
    configs:
    - from: 2020-10-24
    store: tsdb
    object_store: filesystem
    schema: v13
    index:
    prefix: index_
    period: 24h

    #ruler:
    # alertmanager_url: http://localhost:9093

    analytics:
    reporting_enabled: false
    usage_stats_url: ""
    41 changes: 41 additions & 0 deletions populate-loki.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,41 @@
    #!/bin/bash -x

    LOKI_PUSH_URL="http://127.0.0.1:3101/loki/api/v1/push"
    LOKI_MSG_TEMPLATE="{\"streams\": [{\"stream\": {\"job\": \"populate_loki\", \"service_name\": \"test\", \"host\": \"${HOSTNAME}\"}, \"values\": [[\"TIMESTAMP\", \"MESSAGE\"]]}]}"

    _curl_cmd() {
    curl -fsS \
    -H "Content-Type: application/json" \
    -H "Connection: close" \
    "$@"
    }

    # alter timestamp after every 3 messages
    skew_after=3

    # timestamp delta in seconds
    skew_delta=10

    count=0
    while true; do
    count=$(( count + 1 ))
    msg=$(openssl rand -base64 32)

    ts=$(date +%s)
    if (( count > skew_after )); then
    ts=$(( ts - skew_delta ))
    count=0
    fi
    ts_ns=$(( ts * 10**9 ))

    post_body="${LOKI_MSG_TEMPLATE}"
    post_body="${post_body/TIMESTAMP/$ts_ns}"
    post_body="${post_body/MESSAGE/$msg $ts}"

    if ! _curl_cmd -d "${post_body}" "${LOKI_PUSH_URL}"; then
    echo "POST FAILED: '${post_body}'" >&2
    exit 1
    fi

    sleep 1
    done