diff --git a/.github/actions-rs/grcov.yml b/.github/actions-rs/grcov.yml new file mode 100644 index 0000000..dbe2786 --- /dev/null +++ b/.github/actions-rs/grcov.yml @@ -0,0 +1,2 @@ +output-type: lcov +output-file: ./lcov.info \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..a2e2118 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,55 @@ +name: Rust + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - uses: actions-rs/cargo@v1 + with: + command: build + args: --release --verbose + + test: + name: Test with Coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: cachix/install-nix-action@v22 + with: + nix_path: nixpkgs=channel:nixos-20.03 + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + - uses: actions-rs/cargo@v1 + with: + command: llvm-cov + args: --workspace --lcov --output-path lcov.info +# - name: Upload coverage to Codecov +# uses: codecov/codecov-action@v3 +# with: +# token: ${{ secrets.CODECOV_TOKEN }} +# files: lcov.info + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - uses: actions-rs/cargo@v1 + with: + command: clippy + args: --all \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f15bf9a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +/Cargo.lock +test-ledger +.idea diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1f93f10 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "sologger-geyser-plugin" +version = "1.0.0" +edition = "2021" +authors = ["Will Kennedy"] +description = "Solana Geyser Plugin to parse raw logs emitted from a Solana RPC into structured logs and transport Solana logs to either a LogStash or OpenTelemetry endpoint via TCP" +repository = "https://github.com/brytelands/sologger-geyser-plugin" +categories = ["development-tools::debugging"] +license = "Apache-2.0" +keywords = ["solana", "geyser", "logging", "sologger", "otel"] + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +solana-geyser-plugin-interface = "=1.17.34" +solana-logger = "=1.17.34" +solana-sdk = "=1.17.34" +solana-transaction-status = "=1.17.34" +sologger_log_context = "0.1.2" +sologger_log_transformer = "0.1.2" +sologger_log_transport = "0.1.2" +log = "0.4.20" +bs58 = "0.5.1" +log4rs = "1.2.0" +anyhow = "1.0.71" +serde_json = "1.0.117" +serde = { version = "1.0.195", features = ["derive"] } +crossbeam-deque = "0.8.5" + +tokio = { version = "1.0", features = ["sync", "rt"], optional = true } + +[features] +enable_otel = ["sologger_log_transport/otel", "tokio"] +enable_logstash = ["sologger_log_transport/logstash"] \ No newline at end of file diff --git a/Dockerfile-logstash b/Dockerfile-logstash new file mode 100644 index 0000000..9108bc4 --- /dev/null +++ b/Dockerfile-logstash @@ -0,0 +1,84 @@ +# syntax=docker/dockerfile:1 + +# Comments are provided throughout this file to help you get started. +# If you need more help, visit the Dockerfile reference guide at +# https://docs.docker.com/engine/reference/builder/ + +################################################################################ +# Create a stage for building the application. + +ARG RUST_VERSION=1.73.0 +ARG APP_NAME=libsologger_geyser_plugin.so +FROM rust:${RUST_VERSION}-slim-bookworm AS build +ARG APP_NAME +WORKDIR /app + +RUN apt update && apt install -y pkg-config && apt install -y libssl-dev && apt install -y openssl && apt install -y ca-certificates + +# Build the application. +# Leverage a cache mount to /usr/local/cargo/registry/ +# for downloaded dependencies and a cache mount to /app/target/ for +# compiled dependencies which will speed up subsequent builds. +# Leverage a bind mount to the src directory to avoid having to copy the +# source code into the container. Once built, copy the executable to an +# output directory before the cache mounted /app/target is unmounted. +RUN --mount=type=bind,source=src,target=src \ + --mount=type=bind,source=Cargo.toml,target=Cargo.toml \ + --mount=type=bind,source=Cargo.lock,target=Cargo.lock \ + --mount=type=bind,source=dockerfile-config/,target=config/ \ + --mount=type=cache,target=/app/target/ \ + --mount=type=cache,target=/usr/local/cargo/registry/ \ + <20.30}:{L:>3} {h({l})} {m}\n" + +root: + level: error + appenders: + - stdout + +loggers: + sologger_geyser_plugin::log_processor::error: + level: error + appenders: + - stdout + additive: false + sologger_geyser_plugin::log_processor::info: + level: info + appenders: + - stdout + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::update_account: + level: error + appenders: + - stdout + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::notify_block_metadata: + level: error + appenders: + - stdout + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::update_slot_status: + level: error + appenders: + - stdout + additive: false + sologger_geyser_plugin::geyser_plugin_sologger: + level: info + appenders: + - stdout + additive: false \ No newline at end of file diff --git a/config/local/opentelemetry-config.json b/config/local/opentelemetry-config.json new file mode 100644 index 0000000..949ed68 --- /dev/null +++ b/config/local/opentelemetry-config.json @@ -0,0 +1,9 @@ +{ + "logConfig": { + "service.name": "solana-oltp-logging-service" + }, + "endpoint": "http://otel-collector:4317", + "metricsEndpoint": "http://otel-collector:4318/v1/metrics", + "tracesEndpoint": "http://otel-collector:4318/v1/traces", + "logLevel": "INFO" +} \ No newline at end of file diff --git a/config/local/sologger-config.json b/config/local/sologger-config.json new file mode 100644 index 0000000..2b7a7da --- /dev/null +++ b/config/local/sologger-config.json @@ -0,0 +1,11 @@ +{ + "log4rsConfigLocation": "../config/local/log4rs-config.yml", + "opentelemetryConfigLocation": "../config/local/opentelemetry-config.json", + "rpcUrl": "wss://api.devnet.solana.com", + "programsSelector" : { + "programs" : ["BPFLoaderUpgradeab1e11111111111111111111111", "Ed25519SigVerify111111111111111111111111111", "KeccakSecp256k11111111111111111111111111111"] + }, + "accountDataNotificationsEnabled": false, + "transactionNotificationsEnabled": true, + "logProcessorWorkerThreadCount": 2 +} \ No newline at end of file diff --git a/config/sologger-geyser-plugin-config.json b/config/sologger-geyser-plugin-config.json new file mode 100644 index 0000000..bef8af7 --- /dev/null +++ b/config/sologger-geyser-plugin-config.json @@ -0,0 +1,3 @@ +{ + "libpath": "../target/debug/libsologger_geyser_plugin.so" +} \ No newline at end of file diff --git a/docker-examples/README.md b/docker-examples/README.md new file mode 100644 index 0000000..3753e9a --- /dev/null +++ b/docker-examples/README.md @@ -0,0 +1,10 @@ +## Log Management examples with Docker + +**docker-parseable** + +This is the most straightforward way to play around with Sologger or quickly get up and running for development or testing purposes. +This is a simple example of how to use the sologger-logstash image to send logs to Logstash, which then sends the logs to Parseable. + +**docker-signoz** + +This is a example of how to use Signoz to manage Solana logs. It uses the sologger-otel to an OpenTelemetry collector provided by Signoz. \ No newline at end of file diff --git a/docker-examples/docker-parseable/.gitignore b/docker-examples/docker-parseable/.gitignore new file mode 100644 index 0000000..5308b21 --- /dev/null +++ b/docker-examples/docker-parseable/.gitignore @@ -0,0 +1,2 @@ +data +staging \ No newline at end of file diff --git a/docker-examples/docker-parseable/README.md b/docker-examples/docker-parseable/README.md new file mode 100644 index 0000000..a1568da --- /dev/null +++ b/docker-examples/docker-parseable/README.md @@ -0,0 +1,25 @@ +# Sologger with Logstash and Parseable + +**Overview** + +This example shows how to run the sologger plugin with Logstash and Parseable. Be aware, this example is set up to store all the log data locally. So if you are listening to all programs with the log level set to info, you will potentially receive a lot of data. +This example is set up to listen to all programs, but only log those that contain errors. If you want to listen to specific programs, then update the program IDs in the sologger-config.json file. +NOTE: This Parseable example is not setup to handle all logging all the Program logs at info level. + +**Run** + +```shell +docker compose up +``` + +**View Logs** + +Go to http://localhost:8000/login and login with the user name admin and the password admin. The click on the logs link in the left hand navigation bar (http://localhost:8000/solanadevnet/logs). You should see a list of logs. Click on the log to see the details. + +**Query Logs** + +Here is an example of how to query data using the UI: + +```sql +SELECT solana_data_program_id, solana_data_transaction_error, solana_data_errors, solana_data_raw_logs FROM solanadevnet where "level" = 'ERROR'; +``` \ No newline at end of file diff --git a/docker-examples/docker-parseable/docker-compose.yaml b/docker-examples/docker-parseable/docker-compose.yaml new file mode 100644 index 0000000..be5fc35 --- /dev/null +++ b/docker-examples/docker-parseable/docker-compose.yaml @@ -0,0 +1,58 @@ +version: "3.7" + +networks: + parseable: + +services: + + parseable: + image: parseable/parseable:latest + command: ["parseable", "local-store"] + ports: + - "8000:8000" + environment: + - P_FS_DIR=/parseable/data + - P_STAGING_DIR=/parseable/staging + - P_USERNAME=admin + - P_PASSWORD=admin + networks: + - parseable + volumes: + - ./data:/parseable/data + - ./staging:/parseable/staging + + logstash: + image: docker.elastic.co/logstash/logstash-oss:8.10.0-amd64 + ports: + - "9200:9200" + - "50000:50000/tcp" + - "50000:50000/udp" + command: logstash -f /etc/logstash/conf.d/logstash.conf + volumes: + - ./logstash.conf:/etc/logstash/conf.d/logstash.conf + environment: + LS_JAVA_OPTS: "-Xmx512m -Xms512m" + networks: + - parseable + depends_on: + - parseable + + sologger-geyser-plugin: + image: sologger-geyser-plugin + tty: true + ports: + - "1024:1024" + - "9900:9900" + - "8900:8900" + - "8899:8899" + networks: + - parseable + depends_on: + - logstash + volumes: + - type: bind + source: ./log4rs-config.yml + target: /config/local/log4rs-config.yml + - type: bind + source: ./sologger-config.json + target: /config/local/sologger-config.json diff --git a/docker-examples/docker-parseable/log4rs-config.yml b/docker-examples/docker-parseable/log4rs-config.yml new file mode 100644 index 0000000..ce80509 --- /dev/null +++ b/docker-examples/docker-parseable/log4rs-config.yml @@ -0,0 +1,53 @@ +refresh_rate: 30 seconds +appenders: + stdout: + kind: console + encoder: + pattern: "{d(%Y-%m-%d %H:%M:%S):<20} {M:>20.30}:{L:>3} {h({l})} {m}\n" + logstash: + kind: logstash + hostname: logstash + port: 50000 + buffer_size: 100 + log_queue_len: 1000 + buffer_lifetime: 1s + extra_fields: + node_id: "5D1fNXzvv5NjV1ysLjirC4WY92RNsVH18vjmcszZd8on" + node_name: "solana-test-validator-logs" + +root: + level: error + appenders: + - stdout + +loggers: + sologger_geyser_plugin::log_processor::error: + level: error + appenders: + - logstash + additive: false + sologger_geyser_plugin::log_processor::info: + level: info + appenders: + - logstash + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::update_account: + level: error + appenders: + - stdout + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::notify_block_metadata: + level: error + appenders: + - stdout + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::update_slot_status: + level: error + appenders: + - stdout + additive: false + sologger_geyser_plugin::geyser_plugin_sologger: + level: info + appenders: + - stdout + additive: false \ No newline at end of file diff --git a/docker-examples/docker-parseable/logstash.conf b/docker-examples/docker-parseable/logstash.conf new file mode 100644 index 0000000..ee06dae --- /dev/null +++ b/docker-examples/docker-parseable/logstash.conf @@ -0,0 +1,45 @@ +input { + tcp { + port => 50000 + codec => json + } +} + +filter { + json { + skip_on_invalid_json => true + source => "message" + target => "solana_data" + add_tag => [ "_message_json_parsed" ] + } + + if [data] =~ /.*/ { + mutate { + remove_field => [ "data" ] + } + } + + mutate { + remove_field => ["message"] + } +} + +output { + http { + id => "parseable" + format => "json_batch" + codec => "json" + url => "http://parseable:8000/api/v1/ingest" + headers => { + "Authorization" => "Basic YWRtaW46YWRtaW4=" + "x-p-stream" => "solanadevnet" + } + http_method => "post" + http_compression => true + automatic_retries => 5 + retry_non_idempotent => true + connect_timeout => 10 + keepalive => true + content_type => "application/json" + } +} diff --git a/docker-examples/docker-parseable/sologger-config.json b/docker-examples/docker-parseable/sologger-config.json new file mode 100644 index 0000000..7eec4d8 --- /dev/null +++ b/docker-examples/docker-parseable/sologger-config.json @@ -0,0 +1,10 @@ +{ + "log4rsConfigLocation": "../config/local/log4rs-config.yml", + "rpcUrl": "wss://api.devnet.solana.com", + "programsSelector" : { + "programs" : ["*"] + }, + "accountDataNotificationsEnabled": false, + "transactionNotificationsEnabled": true, + "logProcessorWorkerThreadCount": 2 +} \ No newline at end of file diff --git a/docker-examples/docker-signoz/README.md b/docker-examples/docker-signoz/README.md new file mode 100644 index 0000000..c36aaf2 --- /dev/null +++ b/docker-examples/docker-signoz/README.md @@ -0,0 +1,74 @@ +# Deploy + +Check that you have cloned [signoz/signoz](https://github.com/signoz/signoz) +and currently are in `signoz/deploy` folder. + +## Docker + +If you don't have docker set up, please follow [this guide](https://docs.docker.com/engine/install/) +to set up docker before proceeding with the next steps. + +### Using Docker Compose + +If you don't have docker-compose set up, please follow [this guide](https://docs.docker.com/compose/install/) +to set up docker compose before proceeding with the next steps. + +For x86 chip (amd): + +```sh +docker-compose -f docker/clickhouse-setup/docker-compose.yaml up -d +``` + +Open http://localhost:3301 in your favourite browser. In couple of minutes, you should see +the data generated from hotrod in SigNoz UI. + +## Kubernetes + +### Using Helm + +#### Bring up SigNoz cluster + +```sh +helm repo add signoz https://charts.signoz.io + +kubectl create ns platform + +helm -n platform install my-release signoz/signoz +``` + +To access the UI, you can `port-forward` the frontend service: + +```sh +kubectl -n platform port-forward svc/my-release-frontend 3301:3301 +``` + +Open http://localhost:3301 in your favourite browser. Few minutes after you generate load +from the HotROD application, you should see the data generated from hotrod in SigNoz UI. + +#### Test HotROD application with SigNoz + +```sh +kubectl create ns sample-application + +kubectl -n sample-application apply -f https://raw.githubusercontent.com/SigNoz/signoz/develop/sample-apps/hotrod/hotrod.yaml +``` + +To generate load: + +```sh +kubectl -n sample-application run strzal --image=djbingham/curl \ +--restart='OnFailure' -i --tty --rm --command -- curl -X POST -F \ +'user_count=6' -F 'spawn_rate=2' http://locust-master:8089/swarm +``` + +To stop load: + +```sh +kubectl -n sample-application run strzal --image=djbingham/curl \ + --restart='OnFailure' -i --tty --rm --command -- curl \ + http://locust-master:8089/stop +``` + +## Uninstall/Troubleshoot? + +Go to our official documentation site [signoz.io/docs](https://signoz.io/docs) for more. diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/alertmanager.yml b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/alertmanager.yml new file mode 100644 index 0000000..d69357f --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/alertmanager.yml @@ -0,0 +1,35 @@ +global: + resolve_timeout: 1m + slack_api_url: 'https://hooks.slack.com/services/xxx' + +route: + receiver: 'slack-notifications' + +receivers: +- name: 'slack-notifications' + slack_configs: + - channel: '#alerts' + send_resolved: true + icon_url: https://avatars3.githubusercontent.com/u/3380462 + title: |- + [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }} + {{- if gt (len .CommonLabels) (len .GroupLabels) -}} + {{" "}}( + {{- with .CommonLabels.Remove .GroupLabels.Names }} + {{- range $index, $label := .SortedPairs -}} + {{ if $index }}, {{ end }} + {{- $label.Name }}="{{ $label.Value -}}" + {{- end }} + {{- end -}} + ) + {{- end }} + text: >- + {{ range .Alerts -}} + *Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - `{{ .Labels.severity }}`{{ end }} + + *Description:* {{ .Annotations.description }} + + *Details:* + {{ range .Labels.SortedPairs }} • *{{ .Name }}:* `{{ .Value }}` + {{ end }} + {{ end }} \ No newline at end of file diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/alerts.yml b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/alerts.yml new file mode 100644 index 0000000..810a207 --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/alerts.yml @@ -0,0 +1,11 @@ +groups: +- name: ExampleCPULoadGroup + rules: + - alert: HighCpuLoad + expr: system_cpu_load_average_1m > 0.1 + for: 0m + labels: + severity: warning + annotations: + summary: High CPU load + description: "CPU load is > 0.1\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-cluster.xml b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-cluster.xml new file mode 100644 index 0000000..0e3ddcd --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-cluster.xml @@ -0,0 +1,75 @@ + + + + + + zookeeper-1 + 2181 + + + + + + + + + + + + + + + + clickhouse + 9000 + + + + + + + + \ No newline at end of file diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-config.xml b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-config.xml new file mode 100644 index 0000000..dd2b1bd --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-config.xml @@ -0,0 +1,1139 @@ + + + + + + information + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + + 1000M + 10 + + + + + + + + + + + + + + + + + + 8123 + + + 9000 + + + 9004 + + + 9005 + + + + + + + + + + + + 9009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4096 + + + 3 + + + + + false + + + /path/to/ssl_cert_file + /path/to/ssl_key_file + + + false + + + /path/to/ssl_ca_cert_file + + + none + + + 0 + + + -1 + -1 + + + false + + + + + + + + + + + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 100 + + + 0 + + + + 10000 + + + + + + 0.9 + + + 4194304 + + + 0 + + + + + + 8589934592 + + + 5368709120 + + + + 1000 + + + 134217728 + + + 10000 + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + + ` + + + + + + /var/lib/clickhouse/user_files/ + + + + + + + + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + + + default + + + + + + + + + + + + default + + + + + + + + + true + + + false + + ' | sed -e 's|.*>\(.*\)<.*|\1|') + wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge_$PKG_VER-1_all.deb + apt install --no-install-recommends -f ./clickhouse-jdbc-bridge_$PKG_VER-1_all.deb + clickhouse-jdbc-bridge & + + * [CentOS/RHEL] + export MVN_URL=https://repo1.maven.org/maven2/ru/yandex/clickhouse/clickhouse-jdbc-bridge + export PKG_VER=$(curl -sL $MVN_URL/maven-metadata.xml | grep '' | sed -e 's|.*>\(.*\)<.*|\1|') + wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm + yum localinstall -y clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm + clickhouse-jdbc-bridge & + + Please refer to https://github.com/ClickHouse/clickhouse-jdbc-bridge#usage for more information. + ]]> + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + + + + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + query_views_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + system + metric_log
+ 7500 + 1000 +
+ + + + system + asynchronous_metric_log
+ + 7000 +
+ + + + + + engine MergeTree + partition by toYYYYMM(finish_date) + order by (finish_date, finish_time_us, trace_id) + + system + opentelemetry_span_log
+ 7500 +
+ + + + + system + crash_log
+ + + 1000 +
+ + + + + + + system + processors_profile_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + + + + + + *_dictionary.xml + + + *_function.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 60 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + + + hide encrypt/decrypt arguments + ((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\s*\(\s*(?:'(?:\\'|.)+'|.*?)\s*\) + + \1(???) + + + + + + + + + + false + + false + + + https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277 + + + + + + + + + + + 268435456 + true + +
diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-storage.xml b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-storage.xml new file mode 100644 index 0000000..54ec497 --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-storage.xml @@ -0,0 +1,41 @@ + + + + + + 10485760 + + + s3 + + https://BUCKET-NAME.s3-REGION-NAME.amazonaws.com/data/ + ACCESS-KEY-ID + SECRET-ACCESS-KEY + + + + + + + + + + + default + + + s3 + 0 + + + + + + diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-users.xml b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-users.xml new file mode 100644 index 0000000..f185620 --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/clickhouse-users.xml @@ -0,0 +1,123 @@ + + + + + + + + + + 10000000000 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/alertmanager/.gitkeep b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/alertmanager/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/clickhouse-2/.gitkeep b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/clickhouse-2/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/clickhouse-3/.gitkeep b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/clickhouse-3/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/clickhouse/.gitkeep b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/clickhouse/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/signoz/.gitkeep b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/signoz/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/zookeeper-1/.gitkeep b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/zookeeper-1/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/zookeeper-2/.gitkeep b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/zookeeper-2/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/zookeeper-3/.gitkeep b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/data/zookeeper-3/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/docker-compose.yaml b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/docker-compose.yaml new file mode 100644 index 0000000..e6354bb --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -0,0 +1,287 @@ +version: "3.9" + +x-clickhouse-defaults: &clickhouse-defaults + image: clickhouse/clickhouse-server:24.1.2-alpine + tty: true + deploy: + restart_policy: + condition: on-failure + depends_on: + - zookeeper-1 + # - zookeeper-2 + # - zookeeper-3 + logging: + options: + max-size: 50m + max-file: "3" + healthcheck: + # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "0.0.0.0:8123/ping" + ] + interval: 30s + timeout: 5s + retries: 3 + ulimits: + nproc: 65535 + nofile: + soft: 262144 + hard: 262144 + +x-db-depend: &db-depend + depends_on: + - clickhouse + - otel-collector-migrator + # - clickhouse-2 + # - clickhouse-3 + + +services: + zookeeper-1: + image: bitnami/zookeeper:3.7.1 + hostname: zookeeper-1 + user: root + ports: + - "2181:2181" + - "2888:2888" + - "3888:3888" + volumes: + - ./data/zookeeper-1:/bitnami/zookeeper + environment: + - ZOO_SERVER_ID=1 + # - ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888 + - ALLOW_ANONYMOUS_LOGIN=yes + - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-2: + # image: bitnami/zookeeper:3.7.0 + # hostname: zookeeper-2 + # user: root + # ports: + # - "2182:2181" + # - "2889:2888" + # - "3889:3888" + # volumes: + # - ./data/zookeeper-2:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=2 + # - ZOO_SERVERS=zookeeper-1:2888:3888,0.0.0.0:2888:3888,zookeeper-3:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-3: + # image: bitnami/zookeeper:3.7.0 + # hostname: zookeeper-3 + # user: root + # ports: + # - "2183:2181" + # - "2890:2888" + # - "3890:3888" + # volumes: + # - ./data/zookeeper-3:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=3 + # - ZOO_SERVERS=zookeeper-1:2888:3888,zookeeper-2:2888:3888,0.0.0.0:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + clickhouse: + <<: *clickhouse-defaults + hostname: clickhouse + # ports: + # - "9000:9000" + # - "8123:8123" + # - "9181:9181" + volumes: + - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + - ./data/clickhouse/:/var/lib/clickhouse/ + + # clickhouse-2: + # <<: *clickhouse-defaults + # hostname: clickhouse-2 + # ports: + # - "9001:9000" + # - "8124:8123" + # - "9182:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-2/:/var/lib/clickhouse/ + + # clickhouse-3: + # <<: *clickhouse-defaults + # hostname: clickhouse-3 + # ports: + # - "9002:9000" + # - "8125:8123" + # - "9183:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-3/:/var/lib/clickhouse/ + + alertmanager: + image: signoz/alertmanager:0.23.5 + volumes: + - ./data/alertmanager:/data + command: + - --queryService.url=http://query-service:8085 + - --storage.path=/data + depends_on: + - query-service + deploy: + restart_policy: + condition: on-failure + + query-service: + image: signoz/query-service:0.46.0 + command: + [ + "-config=/root/config/prometheus.yml", + # "--prefer-delta=true" + ] + # ports: + # - "6060:6060" # pprof port + # - "8080:8080" # query-service port + volumes: + - ./prometheus.yml:/root/config/prometheus.yml + - ../dashboards:/root/config/dashboards + - ./data/signoz/:/var/lib/signoz/ + environment: + - ClickHouseUrl=tcp://clickhouse:9000 + - ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/ + - SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db + - DASHBOARDS_PATH=/root/config/dashboards + - STORAGE=clickhouse + - GODEBUG=netdns=go + - TELEMETRY_ENABLED=true + - DEPLOYMENT_TYPE=docker-swarm + healthcheck: + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "localhost:8080/api/v1/health" + ] + interval: 30s + timeout: 5s + retries: 3 + deploy: + restart_policy: + condition: on-failure + <<: *db-depend + + frontend: + image: signoz/frontend:0.46.0 + deploy: + restart_policy: + condition: on-failure + depends_on: + - alertmanager + - query-service + ports: + - "3301:3301" + volumes: + - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf + + otel-collector: + image: signoz/signoz-otel-collector:0.88.24 + command: + [ + "--config=/etc/otel-collector-config.yaml", + "--manager-config=/etc/manager-config.yaml", + "--feature-gates=-pkg.translator.prometheus.NormalizeName" + ] + user: root # required for reading docker container logs + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml + - /var/lib/docker/containers:/var/lib/docker/containers:ro + environment: + - OTEL_RESOURCE_ATTRIBUTES=host.name={{.Node.Hostname}},os.type={{.Node.Platform.OS}},dockerswarm.service.name={{.Service.Name}},dockerswarm.task.name={{.Task.Name}} + - DOCKER_MULTI_NODE_CLUSTER=false + - LOW_CARDINAL_EXCEPTION_GROUPING=false + ports: + # - "1777:1777" # pprof extension + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver + # - "8888:8888" # OtelCollector internal metrics + # - "8889:8889" # signoz spanmetrics exposed by the agent + # - "9411:9411" # Zipkin port + # - "13133:13133" # Health check extension + # - "14250:14250" # Jaeger gRPC + # - "14268:14268" # Jaeger thrift HTTP + # - "55678:55678" # OpenCensus receiver + # - "55679:55679" # zPages extension + deploy: + mode: global + restart_policy: + condition: on-failure + depends_on: + - clickhouse + - otel-collector-migrator + - query-service + + otel-collector-migrator: + image: signoz/signoz-schema-migrator:0.88.24 + deploy: + restart_policy: + condition: on-failure + delay: 5s + command: + - "--dsn=tcp://clickhouse:9000" + depends_on: + - clickhouse + # - clickhouse-2 + # - clickhouse-3 + + logspout: + image: "gliderlabs/logspout:v3.2.14" + volumes: + - /etc/hostname:/etc/host_hostname:ro + - /var/run/docker.sock:/var/run/docker.sock + command: syslog+tcp://otel-collector:2255 + depends_on: + - otel-collector + deploy: + mode: global + restart_policy: + condition: on-failure + + hotrod: + image: jaegertracing/example-hotrod:1.30 + command: [ "all" ] + environment: + - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces + logging: + options: + max-size: 50m + max-file: "3" + + load-hotrod: + image: "signoz/locust:1.2.3" + hostname: load-hotrod + environment: + ATTACKED_HOST: http://hotrod:8080 + LOCUST_MODE: standalone + NO_PROXY: standalone + TASK_DELAY_FROM: 5 + TASK_DELAY_TO: 30 + QUIET_MODE: "${QUIET_MODE:-false}" + LOCUST_OPTS: "--headless -u 10 -r 1" + volumes: + - ../common/locust-scripts:/locust diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/docker-entrypoint-initdb.d/init-db.sql b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/docker-entrypoint-initdb.d/init-db.sql new file mode 100644 index 0000000..f71983c --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/docker-entrypoint-initdb.d/init-db.sql @@ -0,0 +1,31 @@ +CREATE TABLE IF NOT EXISTS signoz_index ( + timestamp DateTime64(9) CODEC(Delta, ZSTD(1)), + traceID String CODEC(ZSTD(1)), + spanID String CODEC(ZSTD(1)), + parentSpanID String CODEC(ZSTD(1)), + serviceName LowCardinality(String) CODEC(ZSTD(1)), + name LowCardinality(String) CODEC(ZSTD(1)), + kind Int32 CODEC(ZSTD(1)), + durationNano UInt64 CODEC(ZSTD(1)), + tags Array(String) CODEC(ZSTD(1)), + tagsKeys Array(String) CODEC(ZSTD(1)), + tagsValues Array(String) CODEC(ZSTD(1)), + statusCode Int64 CODEC(ZSTD(1)), + references String CODEC(ZSTD(1)), + externalHttpMethod Nullable(String) CODEC(ZSTD(1)), + externalHttpUrl Nullable(String) CODEC(ZSTD(1)), + component Nullable(String) CODEC(ZSTD(1)), + dbSystem Nullable(String) CODEC(ZSTD(1)), + dbName Nullable(String) CODEC(ZSTD(1)), + dbOperation Nullable(String) CODEC(ZSTD(1)), + peerService Nullable(String) CODEC(ZSTD(1)), + INDEX idx_traceID traceID TYPE bloom_filter GRANULARITY 4, + INDEX idx_service serviceName TYPE bloom_filter GRANULARITY 4, + INDEX idx_name name TYPE bloom_filter GRANULARITY 4, + INDEX idx_kind kind TYPE minmax GRANULARITY 4, + INDEX idx_tagsKeys tagsKeys TYPE bloom_filter(0.01) GRANULARITY 64, + INDEX idx_tagsValues tagsValues TYPE bloom_filter(0.01) GRANULARITY 64, + INDEX idx_duration durationNano TYPE minmax GRANULARITY 1 +) ENGINE MergeTree() +PARTITION BY toDate(timestamp) +ORDER BY (serviceName, -toUnixTimestamp(timestamp)) \ No newline at end of file diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/otel-collector-config.yaml b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/otel-collector-config.yaml new file mode 100644 index 0000000..32cd007 --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/otel-collector-config.yaml @@ -0,0 +1,161 @@ +receivers: + tcplog/docker: + listen_address: "0.0.0.0:2255" + operators: + - type: regex_parser + regex: '^<([0-9]+)>[0-9]+ (?P[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?) (?P\S+) (?P\S+) [0-9]+ - -( (?P.*))?' + timestamp: + parse_from: attributes.timestamp + layout: '%Y-%m-%dT%H:%M:%S.%LZ' + - type: move + from: attributes["body"] + to: body + - type: remove + field: attributes.timestamp + # please remove names from below if you want to collect logs from them + - type: filter + id: signoz_logs_filter + expr: 'attributes.container_name matches "^signoz_(logspout|frontend|alertmanager|query-service|otel-collector|clickhouse|zookeeper)"' + opencensus: + endpoint: 0.0.0.0:55678 + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + jaeger: + protocols: + grpc: + endpoint: 0.0.0.0:14250 + thrift_http: + endpoint: 0.0.0.0:14268 + # thrift_compact: + # endpoint: 0.0.0.0:6831 + # thrift_binary: + # endpoint: 0.0.0.0:6832 + hostmetrics: + collection_interval: 30s + scrapers: + cpu: {} + load: {} + memory: {} + disk: {} + filesystem: {} + network: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + # otel-collector internal metrics + - job_name: otel-collector + static_configs: + - targets: + - localhost:8888 + labels: + job_name: otel-collector + +processors: + batch: + send_batch_size: 10000 + send_batch_max_size: 11000 + timeout: 10s + resourcedetection: + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + detectors: [env, system] # include ec2 for AWS, gcp for GCP and azure for Azure. + timeout: 2s + signozspanmetrics/cumulative: + metrics_exporter: clickhousemetricswrite + latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ] + dimensions_cache_size: 100000 + dimensions: + - name: service.namespace + default: default + - name: deployment.environment + default: default + # This is added to ensure the uniqueness of the timeseries + # Otherwise, identical timeseries produced by multiple replicas of + # collectors result in incorrect APM metrics + - name: 'signoz.collector.id' + # memory_limiter: + # # 80% of maximum memory up to 2G + # limit_mib: 1500 + # # 25% of limit up to 2G + # spike_limit_mib: 512 + # check_interval: 5s + # + # # 50% of the maximum memory + # limit_percentage: 50 + # # 20% of max memory usage spike expected + # spike_limit_percentage: 20 + # queued_retry: + # num_workers: 4 + # queue_size: 100 + # retry_on_failure: true + signozspanmetrics/delta: + metrics_exporter: clickhousemetricswrite + latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ] + dimensions_cache_size: 100000 + aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA + enable_exp_histogram: true + dimensions: + - name: service.namespace + default: default + - name: deployment.environment + default: default + # This is added to ensure the uniqueness of the timeseries + # Otherwise, identical timeseries produced by multiple replicas of + # collectors result in incorrect APM metrics + - name: signoz.collector.id + +exporters: + clickhousetraces: + datasource: tcp://clickhouse:9000/signoz_traces + docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER} + low_cardinal_exception_grouping: ${LOW_CARDINAL_EXCEPTION_GROUPING} + clickhousemetricswrite: + endpoint: tcp://clickhouse:9000/signoz_metrics + resource_to_telemetry_conversion: + enabled: true + clickhousemetricswrite/prometheus: + endpoint: tcp://clickhouse:9000/signoz_metrics + # logging: {} + clickhouselogsexporter: + dsn: tcp://clickhouse:9000/signoz_logs + docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER} + timeout: 10s +extensions: + health_check: + endpoint: 0.0.0.0:13133 + zpages: + endpoint: 0.0.0.0:55679 + pprof: + endpoint: 0.0.0.0:1777 + +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages, pprof] + pipelines: + traces: + receivers: [jaeger, otlp] + processors: [signozspanmetrics/cumulative, signozspanmetrics/delta, batch] + exporters: [clickhousetraces] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [clickhousemetricswrite] + metrics/generic: + receivers: [hostmetrics] + processors: [resourcedetection, batch] + exporters: [clickhousemetricswrite] + metrics/prometheus: + receivers: [prometheus] + processors: [batch] + exporters: [clickhousemetricswrite/prometheus] + logs: + receivers: [otlp, tcplog/docker] + processors: [batch] + exporters: [clickhouselogsexporter] diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/otel-collector-opamp-config.yaml b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/otel-collector-opamp-config.yaml new file mode 100644 index 0000000..e408b55 --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/otel-collector-opamp-config.yaml @@ -0,0 +1 @@ +server_endpoint: ws://query-service:4320/v1/opamp diff --git a/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/prometheus.yml b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/prometheus.yml new file mode 100644 index 0000000..d7c5289 --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/clickhouse-setup/prometheus.yml @@ -0,0 +1,25 @@ +# my global config +global: + scrape_interval: 5s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. + # scrape_timeout is set to the global default (10s). + +# Alertmanager configuration +alerting: + alertmanagers: + - static_configs: + - targets: + - alertmanager:9093 + +# Load rules once and periodically evaluate them according to the global 'evaluation_interval'. +rule_files: + # - "first_rules.yml" + # - "second_rules.yml" + - 'alerts.yml' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: [] + +remote_read: + - url: tcp://clickhouse:9000/signoz_metrics diff --git a/docker-examples/docker-signoz/docker-swarm/common/locust-scripts/locustfile.py b/docker-examples/docker-signoz/docker-swarm/common/locust-scripts/locustfile.py new file mode 100644 index 0000000..0b51820 --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/common/locust-scripts/locustfile.py @@ -0,0 +1,16 @@ +from locust import HttpUser, task, between +class UserTasks(HttpUser): + wait_time = between(5, 15) + + @task + def rachel(self): + self.client.get("/dispatch?customer=123&nonse=0.6308392664170006") + @task + def trom(self): + self.client.get("/dispatch?customer=392&nonse=0.015296363321630757") + @task + def japanese(self): + self.client.get("/dispatch?customer=731&nonse=0.8022286220408668") + @task + def coffee(self): + self.client.get("/dispatch?customer=567&nonse=0.0022220379420636593") diff --git a/docker-examples/docker-signoz/docker-swarm/common/nginx-config.conf b/docker-examples/docker-signoz/docker-swarm/common/nginx-config.conf new file mode 100644 index 0000000..f7943e2 --- /dev/null +++ b/docker-examples/docker-signoz/docker-swarm/common/nginx-config.conf @@ -0,0 +1,51 @@ +server { + listen 3301; + server_name _; + + gzip on; + gzip_static on; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_proxied any; + gzip_vary on; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + + # to handle uri issue 414 from nginx + client_max_body_size 24M; + large_client_header_buffers 8 128k; + + location / { + if ( $uri = '/index.html' ) { + add_header Cache-Control no-store always; + } + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + location ~ ^/api/(v1|v3)/logs/(tail|livetail){ + proxy_pass http://query-service:8080; + proxy_http_version 1.1; + + # connection will be closed if no data is read for 600s between successive read operations + proxy_read_timeout 600s; + + # dont buffer the data send it directly to client. + proxy_buffering off; + proxy_cache off; + } + + location /api { + proxy_pass http://query-service:8080/api; + # connection will be closed if no data is read for 600s between successive read operations + proxy_read_timeout 600s; + } + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file diff --git a/docker-examples/docker-signoz/docker-swarm/dashboards/.gitkeep b/docker-examples/docker-signoz/docker-swarm/dashboards/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/alertmanager.yml b/docker-examples/docker-signoz/docker/clickhouse-setup/alertmanager.yml new file mode 100644 index 0000000..d69357f --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/alertmanager.yml @@ -0,0 +1,35 @@ +global: + resolve_timeout: 1m + slack_api_url: 'https://hooks.slack.com/services/xxx' + +route: + receiver: 'slack-notifications' + +receivers: +- name: 'slack-notifications' + slack_configs: + - channel: '#alerts' + send_resolved: true + icon_url: https://avatars3.githubusercontent.com/u/3380462 + title: |- + [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }} + {{- if gt (len .CommonLabels) (len .GroupLabels) -}} + {{" "}}( + {{- with .CommonLabels.Remove .GroupLabels.Names }} + {{- range $index, $label := .SortedPairs -}} + {{ if $index }}, {{ end }} + {{- $label.Name }}="{{ $label.Value -}}" + {{- end }} + {{- end -}} + ) + {{- end }} + text: >- + {{ range .Alerts -}} + *Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - `{{ .Labels.severity }}`{{ end }} + + *Description:* {{ .Annotations.description }} + + *Details:* + {{ range .Labels.SortedPairs }} • *{{ .Name }}:* `{{ .Value }}` + {{ end }} + {{ end }} \ No newline at end of file diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/alerts.yml b/docker-examples/docker-signoz/docker/clickhouse-setup/alerts.yml new file mode 100644 index 0000000..810a207 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/alerts.yml @@ -0,0 +1,11 @@ +groups: +- name: ExampleCPULoadGroup + rules: + - alert: HighCpuLoad + expr: system_cpu_load_average_1m > 0.1 + for: 0m + labels: + severity: warning + annotations: + summary: High CPU load + description: "CPU load is > 0.1\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-cluster.xml b/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-cluster.xml new file mode 100644 index 0000000..0e3ddcd --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-cluster.xml @@ -0,0 +1,75 @@ + + + + + + zookeeper-1 + 2181 + + + + + + + + + + + + + + + + clickhouse + 9000 + + + + + + + + \ No newline at end of file diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-config.xml b/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-config.xml new file mode 100644 index 0000000..f8213b6 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-config.xml @@ -0,0 +1,1140 @@ + + + + + + information + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + + 1000M + 10 + + + + + + + + + + + + + + + + + + 8123 + + + 9000 + + + 9004 + + + 9005 + + + + + + + + + + + + 9009 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4096 + + + 3 + + + + + false + + + /path/to/ssl_cert_file + /path/to/ssl_key_file + + + false + + + /path/to/ssl_ca_cert_file + + + none + + + 0 + + + -1 + -1 + + + false + + + + + + + + + + + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 100 + + + 0 + + + + 10000 + + + + + + 0.9 + + + 4194304 + + + 0 + + + + + + 8589934592 + + + 5368709120 + + + + 1000 + + + 134217728 + + + 10000 + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + + ` + + + + + + /var/lib/clickhouse/user_files/ + + + + + + + + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + + + default + + + + + + + + + + + + default + + + + + + + + + true + + + false + + ' | sed -e 's|.*>\(.*\)<.*|\1|') + wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge_$PKG_VER-1_all.deb + apt install --no-install-recommends -f ./clickhouse-jdbc-bridge_$PKG_VER-1_all.deb + clickhouse-jdbc-bridge & + + * [CentOS/RHEL] + export MVN_URL=https://repo1.maven.org/maven2/ru/yandex/clickhouse/clickhouse-jdbc-bridge + export PKG_VER=$(curl -sL $MVN_URL/maven-metadata.xml | grep '' | sed -e 's|.*>\(.*\)<.*|\1|') + wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm + yum localinstall -y clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm + clickhouse-jdbc-bridge & + + Please refer to https://github.com/ClickHouse/clickhouse-jdbc-bridge#usage for more information. + ]]> + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + + + + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + query_views_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + system + metric_log
+ 7500 + 1000 +
+ + + + system + asynchronous_metric_log
+ + 7000 +
+ + + + + + engine MergeTree + partition by toYYYYMM(finish_date) + order by (finish_date, finish_time_us, trace_id) + + system + opentelemetry_span_log
+ 7500 +
+ + + + + system + crash_log
+ + + 1000 +
+ + + + + + + system + processors_profile_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + + + + + + *_dictionary.xml + + + *function.xml + /var/lib/clickhouse/user_scripts/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 60 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + + + hide encrypt/decrypt arguments + ((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\s*\(\s*(?:'(?:\\'|.)+'|.*?)\s*\) + + \1(???) + + + + + + + + + + false + + false + + + https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277 + + + + + + + + + + + 268435456 + true + +
diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-storage.xml b/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-storage.xml new file mode 100644 index 0000000..54ec497 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-storage.xml @@ -0,0 +1,41 @@ + + + + + + 10485760 + + + s3 + + https://BUCKET-NAME.s3-REGION-NAME.amazonaws.com/data/ + ACCESS-KEY-ID + SECRET-ACCESS-KEY + + + + + + + + + + + default + + + s3 + 0 + + + + + + diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-users.xml b/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-users.xml new file mode 100644 index 0000000..f185620 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/clickhouse-users.xml @@ -0,0 +1,123 @@ + + + + + + + + + + 10000000000 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/custom-function.xml b/docker-examples/docker-signoz/docker/clickhouse-setup/custom-function.xml new file mode 100644 index 0000000..b2b3f91 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/custom-function.xml @@ -0,0 +1,21 @@ + + + executable + histogramQuantile + Float64 + + Array(Float64) + buckets + + + Array(Float64) + counts + + + Float64 + quantile + + CSV + ./histogramQuantile + + diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/alertmanager/.gitkeep b/docker-examples/docker-signoz/docker/clickhouse-setup/data/alertmanager/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/alertmanager/nflog b/docker-examples/docker-signoz/docker/clickhouse-setup/data/alertmanager/nflog new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/alertmanager/silences b/docker-examples/docker-signoz/docker/clickhouse-setup/data/alertmanager/silences new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/clickhouse-2/.gitkeep b/docker-examples/docker-signoz/docker/clickhouse-setup/data/clickhouse-2/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/clickhouse-3/.gitkeep b/docker-examples/docker-signoz/docker/clickhouse-setup/data/clickhouse-3/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/clickhouse/.gitkeep b/docker-examples/docker-signoz/docker/clickhouse-setup/data/clickhouse/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/signoz/.gitkeep b/docker-examples/docker-signoz/docker/clickhouse-setup/data/signoz/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/signoz/signoz.db b/docker-examples/docker-signoz/docker/clickhouse-setup/data/signoz/signoz.db new file mode 100644 index 0000000..bb6380d Binary files /dev/null and b/docker-examples/docker-signoz/docker/clickhouse-setup/data/signoz/signoz.db differ diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/.gitkeep b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/myid b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/myid new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/myid @@ -0,0 +1 @@ +1 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.3b6 b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.3b6 new file mode 100644 index 0000000..ccfb705 Binary files /dev/null and b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.3b6 differ diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.3e1 b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.3e1 new file mode 100644 index 0000000..ec7ec6c Binary files /dev/null and b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.3e1 differ diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.40c b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.40c new file mode 100644 index 0000000..68d0157 Binary files /dev/null and b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.40c differ diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.462 b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.462 new file mode 100644 index 0000000..67d0e7a Binary files /dev/null and b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/log.462 differ diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/snapshot.3e0 b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/snapshot.3e0 new file mode 100644 index 0000000..b680ed7 Binary files /dev/null and b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/snapshot.3e0 differ diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/snapshot.40b b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/snapshot.40b new file mode 100644 index 0000000..b4e45e9 Binary files /dev/null and b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/snapshot.40b differ diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/snapshot.461 b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/snapshot.461 new file mode 100644 index 0000000..bfbf200 Binary files /dev/null and b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-1/data/version-2/snapshot.461 differ diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-2/.gitkeep b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-2/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-3/.gitkeep b/docker-examples/docker-signoz/docker/clickhouse-setup/data/zookeeper-3/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose-core.yaml b/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose-core.yaml new file mode 100644 index 0000000..cf1e5f1 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose-core.yaml @@ -0,0 +1,154 @@ +version: "2.4" + +services: + zookeeper-1: + image: bitnami/zookeeper:3.7.1 + container_name: signoz-zookeeper-1 + hostname: zookeeper-1 + user: root + ports: + - "2181:2181" + - "2888:2888" + - "3888:3888" + volumes: + - ./data/zookeeper-1:/bitnami/zookeeper + environment: + - ZOO_SERVER_ID=1 + # - ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888 + - ALLOW_ANONYMOUS_LOGIN=yes + - ZOO_AUTOPURGE_INTERVAL=1 + + clickhouse: + image: clickhouse/clickhouse-server:24.1.2-alpine + container_name: signoz-clickhouse + # ports: + # - "9000:9000" + # - "8123:8123" + tty: true + volumes: + - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + - ./data/clickhouse/:/var/lib/clickhouse/ + - ./user_scripts:/var/lib/clickhouse/user_scripts/ + restart: on-failure + logging: + options: + max-size: 50m + max-file: "3" + healthcheck: + # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "0.0.0.0:8123/ping" + ] + interval: 30s + timeout: 5s + retries: 3 + + alertmanager: + container_name: signoz-alertmanager + image: signoz/alertmanager:0.23.5 + volumes: + - ./data/alertmanager:/data + depends_on: + query-service: + condition: service_healthy + restart: on-failure + command: + - --queryService.url=http://query-service:8085 + - --storage.path=/data + + otel-collector-migrator: + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24} + container_name: otel-migrator + command: + - "--dsn=tcp://clickhouse:9000" + depends_on: + clickhouse: + condition: service_healthy + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + + # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` + otel-collector: + container_name: signoz-otel-collector + image: signoz/signoz-otel-collector:0.88.24 + command: + [ + "--config=/etc/otel-collector-config.yaml", + "--manager-config=/etc/manager-config.yaml", + "--copy-path=/var/tmp/collector-config.yaml", + "--feature-gates=-pkg.translator.prometheus.NormalizeName" + ] + # user: root # required for reading docker container logs + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml + environment: + - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux + ports: + # - "1777:1777" # pprof extension + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver + # - "8888:8888" # OtelCollector internal metrics + # - "8889:8889" # signoz spanmetrics exposed by the agent + # - "9411:9411" # Zipkin port + # - "13133:13133" # health check extension + # - "14250:14250" # Jaeger gRPC + # - "14268:14268" # Jaeger thrift HTTP + # - "55678:55678" # OpenCensus receiver + # - "55679:55679" # zPages extension + restart: on-failure + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + query-service: + condition: service_healthy + + logspout: + image: "gliderlabs/logspout:v3.2.14" + container_name: signoz-logspout + volumes: + - /etc/hostname:/etc/host_hostname:ro + - /var/run/docker.sock:/var/run/docker.sock + command: syslog+tcp://otel-collector:2255 + depends_on: + - otel-collector + restart: on-failure + + hotrod: + image: jaegertracing/example-hotrod:1.30 + container_name: hotrod + logging: + options: + max-size: 50m + max-file: "3" + command: [ "all" ] + environment: + - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces + + load-hotrod: + image: "signoz/locust:1.2.3" + container_name: load-hotrod + hostname: load-hotrod + environment: + ATTACKED_HOST: http://hotrod:8080 + LOCUST_MODE: standalone + NO_PROXY: standalone + TASK_DELAY_FROM: 5 + TASK_DELAY_TO: 30 + QUIET_MODE: "${QUIET_MODE:-false}" + LOCUST_OPTS: "--headless -u 10 -r 1" + volumes: + - ../common/locust-scripts:/locust diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose-local.yaml b/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose-local.yaml new file mode 100644 index 0000000..248c7bf --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose-local.yaml @@ -0,0 +1,67 @@ +version: "2.4" + +services: + query-service: + hostname: query-service + build: + context: "../../../" + dockerfile: "./pkg/query-service/Dockerfile" + args: + LDFLAGS: "" + TARGETPLATFORM: "${GOOS}/${GOARCH}" + container_name: signoz-query-service + environment: + - ClickHouseUrl=tcp://clickhouse:9000 + - ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/ + - SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db + - DASHBOARDS_PATH=/root/config/dashboards + - STORAGE=clickhouse + - GODEBUG=netdns=go + - TELEMETRY_ENABLED=true + volumes: + - ./prometheus.yml:/root/config/prometheus.yml + - ../dashboards:/root/config/dashboards + - ./data/signoz/:/var/lib/signoz/ + command: + [ + "-config=/root/config/prometheus.yml", + # "--prefer-delta=true" + ] + ports: + - "6060:6060" + - "8080:8080" + restart: on-failure + healthcheck: + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "localhost:8080/api/v1/health" + ] + interval: 30s + timeout: 5s + retries: 3 + depends_on: + clickhouse: + condition: service_healthy + + frontend: + build: + context: "../../../frontend" + dockerfile: "./Dockerfile" + args: + TARGETOS: "${GOOS}" + TARGETPLATFORM: "${GOARCH}" + container_name: signoz-frontend + environment: + - FRONTEND_API_ENDPOINT=http://query-service:8080 + restart: on-failure + depends_on: + - alertmanager + - query-service + ports: + - "3301:3301" + volumes: + - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose.testing.yaml b/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose.testing.yaml new file mode 100644 index 0000000..efd6198 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose.testing.yaml @@ -0,0 +1,307 @@ +version: "2.4" + +x-clickhouse-defaults: &clickhouse-defaults + restart: on-failure + # addding non LTS version due to this fix https://github.com/ClickHouse/ClickHouse/commit/32caf8716352f45c1b617274c7508c86b7d1afab + image: clickhouse/clickhouse-server:24.1.2-alpine + tty: true + depends_on: + - zookeeper-1 + # - zookeeper-2 + # - zookeeper-3 + logging: + options: + max-size: 50m + max-file: "3" + healthcheck: + # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "0.0.0.0:8123/ping" + ] + interval: 30s + timeout: 5s + retries: 3 + ulimits: + nproc: 65535 + nofile: + soft: 262144 + hard: 262144 + +x-db-depend: &db-depend + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + +services: + + zookeeper-1: + image: bitnami/zookeeper:3.7.1 + container_name: signoz-zookeeper-1 + hostname: zookeeper-1 + user: root + ports: + - "2181:2181" + - "2888:2888" + - "3888:3888" + volumes: + - ./data/zookeeper-1:/bitnami/zookeeper + environment: + - ZOO_SERVER_ID=1 + # - ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888 + - ALLOW_ANONYMOUS_LOGIN=yes + - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-2: + # image: bitnami/zookeeper:3.7.0 + # container_name: signoz-zookeeper-2 + # hostname: zookeeper-2 + # user: root + # ports: + # - "2182:2181" + # - "2889:2888" + # - "3889:3888" + # volumes: + # - ./data/zookeeper-2:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=2 + # - ZOO_SERVERS=zookeeper-1:2888:3888,0.0.0.0:2888:3888,zookeeper-3:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-3: + # image: bitnami/zookeeper:3.7.0 + # container_name: signoz-zookeeper-3 + # hostname: zookeeper-3 + # user: root + # ports: + # - "2183:2181" + # - "2890:2888" + # - "3890:3888" + # volumes: + # - ./data/zookeeper-3:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=3 + # - ZOO_SERVERS=zookeeper-1:2888:3888,zookeeper-2:2888:3888,0.0.0.0:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + clickhouse: + <<: *clickhouse-defaults + container_name: signoz-clickhouse + hostname: clickhouse + ports: + - "9000:9000" + - "8123:8123" + - "9181:9181" + volumes: + - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + - ./data/clickhouse/:/var/lib/clickhouse/ + - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + # clickhouse-2: + # <<: *clickhouse-defaults + # container_name: signoz-clickhouse-2 + # hostname: clickhouse-2 + # ports: + # - "9001:9000" + # - "8124:8123" + # - "9182:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-2/:/var/lib/clickhouse/ + # - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + + # clickhouse-3: + # <<: *clickhouse-defaults + # container_name: signoz-clickhouse-3 + # hostname: clickhouse-3 + # ports: + # - "9002:9000" + # - "8125:8123" + # - "9183:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-3/:/var/lib/clickhouse/ + # - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + alertmanager: + image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.5} + container_name: signoz-alertmanager + volumes: + - ./data/alertmanager:/data + depends_on: + query-service: + condition: service_healthy + restart: on-failure + command: + - --queryService.url=http://query-service:8085 + - --storage.path=/data + + # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` + + query-service: + image: signoz/query-service:${DOCKER_TAG:-0.46.0} + container_name: signoz-query-service + command: + [ + "-config=/root/config/prometheus.yml", + "-gateway-url=https://api.staging.signoz.cloud" + # "--prefer-delta=true" + ] + # ports: + # - "6060:6060" # pprof port + # - "8080:8080" # query-service port + volumes: + - ./prometheus.yml:/root/config/prometheus.yml + - ../dashboards:/root/config/dashboards + - ./data/signoz/:/var/lib/signoz/ + environment: + - ClickHouseUrl=tcp://clickhouse:9000 + - ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/ + - SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db + - DASHBOARDS_PATH=/root/config/dashboards + - STORAGE=clickhouse + - GODEBUG=netdns=go + - TELEMETRY_ENABLED=true + - DEPLOYMENT_TYPE=docker-standalone-amd + restart: on-failure + healthcheck: + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "localhost:8080/api/v1/health" + ] + interval: 30s + timeout: 5s + retries: 3 + <<: *db-depend + + frontend: + image: signoz/frontend:${DOCKER_TAG:-0.46.0} + container_name: signoz-frontend + restart: on-failure + depends_on: + - alertmanager + - query-service + ports: + - "3301:3301" + volumes: + - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf + + otel-collector-migrator: + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24} + container_name: otel-migrator + command: + - "--dsn=tcp://clickhouse:9000" + depends_on: + clickhouse: + condition: service_healthy + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + + + otel-collector: + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.24} + container_name: signoz-otel-collector + command: + [ + "--config=/etc/otel-collector-config.yaml", + "--manager-config=/etc/manager-config.yaml", + "--copy-path=/var/tmp/collector-config.yaml", + "--feature-gates=-pkg.translator.prometheus.NormalizeName" + ] + user: root # required for reading docker container logs + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml + - /var/lib/docker/containers:/var/lib/docker/containers:ro + environment: + - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux + - DOCKER_MULTI_NODE_CLUSTER=false + - LOW_CARDINAL_EXCEPTION_GROUPING=false + ports: + # - "1777:1777" # pprof extension + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver + # - "8888:8888" # OtelCollector internal metrics + # - "8889:8889" # signoz spanmetrics exposed by the agent + # - "9411:9411" # Zipkin port + # - "13133:13133" # health check extension + # - "14250:14250" # Jaeger gRPC + # - "14268:14268" # Jaeger thrift HTTP + # - "55678:55678" # OpenCensus receiver + # - "55679:55679" # zPages extension + restart: on-failure + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + query-service: + condition: service_healthy + + logspout: + image: "gliderlabs/logspout:v3.2.14" + container_name: signoz-logspout + volumes: + - /etc/hostname:/etc/host_hostname:ro + - /var/run/docker.sock:/var/run/docker.sock + command: syslog+tcp://otel-collector:2255 + depends_on: + - otel-collector + restart: on-failure + + hotrod: + image: jaegertracing/example-hotrod:1.30 + container_name: hotrod + logging: + options: + max-size: 50m + max-file: "3" + command: [ "all" ] + environment: + - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces + + load-hotrod: + image: "signoz/locust:1.2.3" + container_name: load-hotrod + hostname: load-hotrod + environment: + ATTACKED_HOST: http://hotrod:8080 + LOCUST_MODE: standalone + NO_PROXY: standalone + TASK_DELAY_FROM: 5 + TASK_DELAY_TO: 30 + QUIET_MODE: "${QUIET_MODE:-false}" + LOCUST_OPTS: "--headless -u 10 -r 1" + volumes: + - ../common/locust-scripts:/locust diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose.yaml b/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose.yaml new file mode 100644 index 0000000..6de1a7d --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/docker-compose.yaml @@ -0,0 +1,302 @@ +version: "3.7" + +x-clickhouse-defaults: &clickhouse-defaults + restart: on-failure + # addding non LTS version due to this fix https://github.com/ClickHouse/ClickHouse/commit/32caf8716352f45c1b617274c7508c86b7d1afab + image: clickhouse/clickhouse-server:24.1.2-alpine + tty: true + depends_on: + - zookeeper-1 + # - zookeeper-2 + # - zookeeper-3 + logging: + options: + max-size: 50m + max-file: "3" + healthcheck: + # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "0.0.0.0:8123/ping" + ] + interval: 30s + timeout: 5s + retries: 3 + ulimits: + nproc: 65535 + nofile: + soft: 262144 + hard: 262144 + +x-db-depend: &db-depend + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + +services: + + zookeeper-1: + image: bitnami/zookeeper:3.7.1 + container_name: signoz-zookeeper-1 + hostname: zookeeper-1 + user: root + ports: + - "2181:2181" + - "2888:2888" + - "3888:3888" + volumes: + - ./data/zookeeper-1:/bitnami/zookeeper + environment: + - ZOO_SERVER_ID=1 + # - ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888 + - ALLOW_ANONYMOUS_LOGIN=yes + - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-2: + # image: bitnami/zookeeper:3.7.0 + # container_name: signoz-zookeeper-2 + # hostname: zookeeper-2 + # user: root + # ports: + # - "2182:2181" + # - "2889:2888" + # - "3889:3888" + # volumes: + # - ./data/zookeeper-2:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=2 + # - ZOO_SERVERS=zookeeper-1:2888:3888,0.0.0.0:2888:3888,zookeeper-3:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-3: + # image: bitnami/zookeeper:3.7.0 + # container_name: signoz-zookeeper-3 + # hostname: zookeeper-3 + # user: root + # ports: + # - "2183:2181" + # - "2890:2888" + # - "3890:3888" + # volumes: + # - ./data/zookeeper-3:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=3 + # - ZOO_SERVERS=zookeeper-1:2888:3888,zookeeper-2:2888:3888,0.0.0.0:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + clickhouse: + <<: *clickhouse-defaults + container_name: signoz-clickhouse + hostname: clickhouse + ports: + - "9000:9000" + - "8123:8123" + - "9181:9181" + volumes: + - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + - ./data/clickhouse/:/var/lib/clickhouse/ + - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + # clickhouse-2: + # <<: *clickhouse-defaults + # container_name: signoz-clickhouse-2 + # hostname: clickhouse-2 + # ports: + # - "9001:9000" + # - "8124:8123" + # - "9182:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-2/:/var/lib/clickhouse/ + # - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + + # clickhouse-3: + # <<: *clickhouse-defaults + # container_name: signoz-clickhouse-3 + # hostname: clickhouse-3 + # ports: + # - "9002:9000" + # - "8125:8123" + # - "9183:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-3/:/var/lib/clickhouse/ + # - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + alertmanager: + image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.5} + container_name: signoz-alertmanager + volumes: + - ./data/alertmanager:/data + depends_on: + query-service: + condition: service_healthy + restart: on-failure + command: + - --queryService.url=http://query-service:8085 + - --storage.path=/data + + # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` + + query-service: + image: signoz/query-service:${DOCKER_TAG:-0.46.0} + container_name: signoz-query-service + command: + [ + "-config=/root/config/prometheus.yml" + # "--prefer-delta=true" + ] + # ports: + # - "6060:6060" # pprof port + # - "8080:8080" # query-service port + volumes: + - ./prometheus.yml:/root/config/prometheus.yml + - ../dashboards:/root/config/dashboards + - ./data/signoz/:/var/lib/signoz/ + environment: + - ClickHouseUrl=tcp://clickhouse:9000 + - ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/ + - SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db + - DASHBOARDS_PATH=/root/config/dashboards + - STORAGE=clickhouse + - GODEBUG=netdns=go + - TELEMETRY_ENABLED=true + - DEPLOYMENT_TYPE=docker-standalone-amd + restart: on-failure + healthcheck: + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "localhost:8080/api/v1/health" + ] + interval: 30s + timeout: 5s + retries: 3 + <<: *db-depend + + frontend: + image: signoz/frontend:${DOCKER_TAG:-0.46.0} + container_name: signoz-frontend + restart: on-failure + depends_on: + - alertmanager + - query-service + ports: + - "3301:3301" + volumes: + - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf + + otel-collector-migrator: + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24} + container_name: otel-migrator + command: + - "--dsn=tcp://clickhouse:9000" + depends_on: + clickhouse: + condition: service_healthy + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + + + otel-collector: + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.24} + container_name: signoz-otel-collector + command: + [ + "--config=/etc/otel-collector-config.yaml", + "--manager-config=/etc/manager-config.yaml", + "--copy-path=/var/tmp/collector-config.yaml", + "--feature-gates=-pkg.translator.prometheus.NormalizeName" + ] + user: root # required for reading docker container logs + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml + - /var/lib/docker/containers:/var/lib/docker/containers:ro + environment: + - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux + - DOCKER_MULTI_NODE_CLUSTER=false + - LOW_CARDINAL_EXCEPTION_GROUPING=false + ports: + # - "1777:1777" # pprof extension + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver + # - "8888:8888" # OtelCollector internal metrics + # - "8889:8889" # signoz spanmetrics exposed by the agent + # - "9411:9411" # Zipkin port + # - "13133:13133" # health check extension + # - "14250:14250" # Jaeger gRPC + # - "14268:14268" # Jaeger thrift HTTP + # - "55678:55678" # OpenCensus receiver + # - "55679:55679" # zPages extension + restart: on-failure + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + query-service: + condition: service_healthy + + logspout: + image: "gliderlabs/logspout:v3.2.14" + container_name: signoz-logspout + volumes: + - /etc/hostname:/etc/host_hostname:ro + - /var/run/docker.sock:/var/run/docker.sock + command: syslog+tcp://otel-collector:2255 + depends_on: + - otel-collector + restart: on-failure + + sologger-otel-geyser-plugin: + image: sologger-otel-geyser-plugin + container_name: solana-test-validator-sologger-otel + restart: on-failure + ports: + - "1024:1024" + - "9900:9900" + - "8900:8900" + - "8899:8899" + depends_on: + - otel-collector + volumes: + - type: bind + source: ./sologger/log4rs-config.yml + target: /config/local/log4rs-config.yml + - type: bind + source: ./sologger/opentelemetry-config.json + target: /config/local/opentelemetry-config.json + - type: bind + source: ./sologger/sologger-config.json + target: /config/local/sologger-config.json diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/keeper_config.xml b/docker-examples/docker-signoz/docker/clickhouse-setup/keeper_config.xml new file mode 100644 index 0000000..a9a25c1 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/keeper_config.xml @@ -0,0 +1,64 @@ + + + + information + /var/log/clickhouse-keeper/clickhouse-keeper.log + /var/log/clickhouse-keeper/clickhouse-keeper.err.log + + 1000M + 10 + + + + 0.0.0.0 + 4096 + + + 9181 + + + 1 + + /var/lib/clickhouse/coordination/logs + /var/lib/clickhouse/coordination/snapshots + + + 10000 + 10000 + 100000 + information + false + + + + + true + + + 1 + + + clickhouses-keeper-1 + 9234 + + + + + + + \ No newline at end of file diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/otel-collector-config.yaml b/docker-examples/docker-signoz/docker/clickhouse-setup/otel-collector-config.yaml new file mode 100644 index 0000000..7a103d1 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/otel-collector-config.yaml @@ -0,0 +1,168 @@ +receivers: + tcplog/docker: + listen_address: "0.0.0.0:2255" + operators: + - type: regex_parser + regex: '^<([0-9]+)>[0-9]+ (?P[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?) (?P\S+) (?P\S+) [0-9]+ - -( (?P.*))?' + timestamp: + parse_from: attributes.timestamp + layout: '%Y-%m-%dT%H:%M:%S.%LZ' + - type: move + from: attributes["body"] + to: body + - type: remove + field: attributes.timestamp + # please remove names from below if you want to collect logs from them + - type: filter + id: signoz_logs_filter + expr: 'attributes.container_name matches "^signoz-(logspout|frontend|alertmanager|query-service|otel-collector|clickhouse|zookeeper)"' + opencensus: + endpoint: 0.0.0.0:55678 + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + jaeger: + protocols: + grpc: + endpoint: 0.0.0.0:14250 + thrift_http: + endpoint: 0.0.0.0:14268 + # thrift_compact: + # endpoint: 0.0.0.0:6831 + # thrift_binary: + # endpoint: 0.0.0.0:6832 + hostmetrics: + collection_interval: 30s + scrapers: + cpu: {} + load: {} + memory: {} + disk: {} + filesystem: {} + network: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + # otel-collector internal metrics + - job_name: otel-collector + static_configs: + - targets: + - localhost:8888 + labels: + job_name: otel-collector + + +processors: + batch: + send_batch_size: 10000 + send_batch_max_size: 11000 + timeout: 10s + signozspanmetrics/cumulative: + metrics_exporter: clickhousemetricswrite + metrics_flush_interval: 60s + latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ] + dimensions_cache_size: 100000 + dimensions: + - name: service.namespace + default: default + - name: deployment.environment + default: default + # This is added to ensure the uniqueness of the timeseries + # Otherwise, identical timeseries produced by multiple replicas of + # collectors result in incorrect APM metrics + - name: 'signoz.collector.id' + # memory_limiter: + # # 80% of maximum memory up to 2G + # limit_mib: 1500 + # # 25% of limit up to 2G + # spike_limit_mib: 512 + # check_interval: 5s + # + # # 50% of the maximum memory + # limit_percentage: 50 + # # 20% of max memory usage spike expected + # spike_limit_percentage: 20 + # queued_retry: + # num_workers: 4 + # queue_size: 100 + # retry_on_failure: true + resourcedetection: + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + detectors: [env, system] # include ec2 for AWS, gcp for GCP and azure for Azure. + timeout: 2s + signozspanmetrics/delta: + metrics_exporter: clickhousemetricswrite + metrics_flush_interval: 60s + latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ] + dimensions_cache_size: 100000 + aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA + enable_exp_histogram: true + dimensions: + - name: service.namespace + default: default + - name: deployment.environment + default: default + # This is added to ensure the uniqueness of the timeseries + # Otherwise, identical timeseries produced by multiple replicas of + # collectors result in incorrect APM metrics + - name: signoz.collector.id + +extensions: + health_check: + endpoint: 0.0.0.0:13133 + zpages: + endpoint: 0.0.0.0:55679 + pprof: + endpoint: 0.0.0.0:1777 + +exporters: + clickhousetraces: + datasource: tcp://clickhouse:9000/signoz_traces + docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER} + low_cardinal_exception_grouping: ${LOW_CARDINAL_EXCEPTION_GROUPING} + clickhousemetricswrite: + endpoint: tcp://clickhouse:9000/signoz_metrics + resource_to_telemetry_conversion: + enabled: true + clickhousemetricswrite/prometheus: + endpoint: tcp://clickhouse:9000/signoz_metrics + clickhouselogsexporter: + dsn: tcp://clickhouse:9000/signoz_logs + docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER} + timeout: 10s + # logging: {} + +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: + - health_check + - zpages + - pprof + pipelines: + traces: + receivers: [jaeger, otlp] + processors: [signozspanmetrics/cumulative, signozspanmetrics/delta, batch] + exporters: [clickhousetraces] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [clickhousemetricswrite] + metrics/generic: + receivers: [hostmetrics] + processors: [resourcedetection, batch] + exporters: [clickhousemetricswrite] + metrics/prometheus: + receivers: [prometheus] + processors: [batch] + exporters: [clickhousemetricswrite/prometheus] + logs: + receivers: [otlp, tcplog/docker] + processors: [batch] + exporters: [clickhouselogsexporter] \ No newline at end of file diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/otel-collector-opamp-config.yaml b/docker-examples/docker-signoz/docker/clickhouse-setup/otel-collector-opamp-config.yaml new file mode 100644 index 0000000..e408b55 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/otel-collector-opamp-config.yaml @@ -0,0 +1 @@ +server_endpoint: ws://query-service:4320/v1/opamp diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/prometheus.yml b/docker-examples/docker-signoz/docker/clickhouse-setup/prometheus.yml new file mode 100644 index 0000000..d7c5289 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/prometheus.yml @@ -0,0 +1,25 @@ +# my global config +global: + scrape_interval: 5s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. + # scrape_timeout is set to the global default (10s). + +# Alertmanager configuration +alerting: + alertmanagers: + - static_configs: + - targets: + - alertmanager:9093 + +# Load rules once and periodically evaluate them according to the global 'evaluation_interval'. +rule_files: + # - "first_rules.yml" + # - "second_rules.yml" + - 'alerts.yml' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: [] + +remote_read: + - url: tcp://clickhouse:9000/signoz_metrics diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/sologger/log4rs-config.yml b/docker-examples/docker-signoz/docker/clickhouse-setup/sologger/log4rs-config.yml new file mode 100644 index 0000000..8156e6b --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/sologger/log4rs-config.yml @@ -0,0 +1,37 @@ +refresh_rate: 30 seconds +appenders: + stdout: + kind: console + encoder: + pattern: "{d(%Y-%m-%d %H:%M:%S):<20} {M:>20.30}:{L:>3} {h({l})} {m}\n" + +root: + level: error + appenders: + - stdout + +loggers: + sologger_geyser_plugin::log_processor::error: + level: error + appenders: + additive: false + sologger_geyser_plugin::log_processor::info: + level: info + appenders: + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::update_account: + level: errore + appenders: + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::notify_block_metadata: + level: error + appenders: + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::update_slot_status: + level: error + appenders: + additive: false + sologger_geyser_plugin::geyser_plugin_sologger: + level: error + appenders: + additive: false \ No newline at end of file diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/sologger/opentelemetry-config.json b/docker-examples/docker-signoz/docker/clickhouse-setup/sologger/opentelemetry-config.json new file mode 100644 index 0000000..949ed68 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/sologger/opentelemetry-config.json @@ -0,0 +1,9 @@ +{ + "logConfig": { + "service.name": "solana-oltp-logging-service" + }, + "endpoint": "http://otel-collector:4317", + "metricsEndpoint": "http://otel-collector:4318/v1/metrics", + "tracesEndpoint": "http://otel-collector:4318/v1/traces", + "logLevel": "INFO" +} \ No newline at end of file diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/sologger/sologger-config.json b/docker-examples/docker-signoz/docker/clickhouse-setup/sologger/sologger-config.json new file mode 100644 index 0000000..dfd9260 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/sologger/sologger-config.json @@ -0,0 +1,11 @@ +{ + "log4rsConfigLocation": "../config/local/log4rs-config.yml", + "opentelemetryConfigLocation": "../config/local/opentelemetry-config.json", + "rpcUrl": "wss://api.devnet.solana.com", + "programsSelector" : { + "programs" : ["*"] + }, + "accountDataNotificationsEnabled": false, + "transactionNotificationsEnabled": true, + "logProcessorWorkerThreadCount": 3 +} \ No newline at end of file diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/user_scripts/histogramQuantile b/docker-examples/docker-signoz/docker/clickhouse-setup/user_scripts/histogramQuantile new file mode 100644 index 0000000..3b77a7b Binary files /dev/null and b/docker-examples/docker-signoz/docker/clickhouse-setup/user_scripts/histogramQuantile differ diff --git a/docker-examples/docker-signoz/docker/clickhouse-setup/user_scripts/histogramQuantile.go b/docker-examples/docker-signoz/docker/clickhouse-setup/user_scripts/histogramQuantile.go new file mode 100644 index 0000000..9540a77 --- /dev/null +++ b/docker-examples/docker-signoz/docker/clickhouse-setup/user_scripts/histogramQuantile.go @@ -0,0 +1,237 @@ +package main + +import ( + "bufio" + "fmt" + "math" + "os" + "sort" + "strconv" + "strings" +) + +// NOTE: executable must be built with target OS and architecture set to linux/amd64 +// env GOOS=linux GOARCH=amd64 go build -o histogramQuantile histogramQuantile.go + +// The following code is adapted from the following source: +// https://github.com/prometheus/prometheus/blob/main/promql/quantile.go + +type bucket struct { + upperBound float64 + count float64 +} + +// buckets implements sort.Interface. +type buckets []bucket + +func (b buckets) Len() int { return len(b) } +func (b buckets) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b buckets) Less(i, j int) bool { return b[i].upperBound < b[j].upperBound } + +// bucketQuantile calculates the quantile 'q' based on the given buckets. The +// buckets will be sorted by upperBound by this function (i.e. no sorting +// needed before calling this function). The quantile value is interpolated +// assuming a linear distribution within a bucket. However, if the quantile +// falls into the highest bucket, the upper bound of the 2nd highest bucket is +// returned. A natural lower bound of 0 is assumed if the upper bound of the +// lowest bucket is greater 0. In that case, interpolation in the lowest bucket +// happens linearly between 0 and the upper bound of the lowest bucket. +// However, if the lowest bucket has an upper bound less or equal 0, this upper +// bound is returned if the quantile falls into the lowest bucket. +// +// There are a number of special cases (once we have a way to report errors +// happening during evaluations of AST functions, we should report those +// explicitly): +// +// If 'buckets' has 0 observations, NaN is returned. +// +// If 'buckets' has fewer than 2 elements, NaN is returned. +// +// If the highest bucket is not +Inf, NaN is returned. +// +// If q==NaN, NaN is returned. +// +// If q<0, -Inf is returned. +// +// If q>1, +Inf is returned. +func bucketQuantile(q float64, buckets buckets) float64 { + if math.IsNaN(q) { + return math.NaN() + } + if q < 0 { + return math.Inf(-1) + } + if q > 1 { + return math.Inf(+1) + } + sort.Sort(buckets) + if !math.IsInf(buckets[len(buckets)-1].upperBound, +1) { + return math.NaN() + } + + buckets = coalesceBuckets(buckets) + ensureMonotonic(buckets) + + if len(buckets) < 2 { + return math.NaN() + } + observations := buckets[len(buckets)-1].count + if observations == 0 { + return math.NaN() + } + rank := q * observations + b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].count >= rank }) + + if b == len(buckets)-1 { + return buckets[len(buckets)-2].upperBound + } + if b == 0 && buckets[0].upperBound <= 0 { + return buckets[0].upperBound + } + var ( + bucketStart float64 + bucketEnd = buckets[b].upperBound + count = buckets[b].count + ) + if b > 0 { + bucketStart = buckets[b-1].upperBound + count -= buckets[b-1].count + rank -= buckets[b-1].count + } + return bucketStart + (bucketEnd-bucketStart)*(rank/count) +} + +// coalesceBuckets merges buckets with the same upper bound. +// +// The input buckets must be sorted. +func coalesceBuckets(buckets buckets) buckets { + last := buckets[0] + i := 0 + for _, b := range buckets[1:] { + if b.upperBound == last.upperBound { + last.count += b.count + } else { + buckets[i] = last + last = b + i++ + } + } + buckets[i] = last + return buckets[:i+1] +} + +// The assumption that bucket counts increase monotonically with increasing +// upperBound may be violated during: +// +// * Recording rule evaluation of histogram_quantile, especially when rate() +// has been applied to the underlying bucket timeseries. +// * Evaluation of histogram_quantile computed over federated bucket +// timeseries, especially when rate() has been applied. +// +// This is because scraped data is not made available to rule evaluation or +// federation atomically, so some buckets are computed with data from the +// most recent scrapes, but the other buckets are missing data from the most +// recent scrape. +// +// Monotonicity is usually guaranteed because if a bucket with upper bound +// u1 has count c1, then any bucket with a higher upper bound u > u1 must +// have counted all c1 observations and perhaps more, so that c >= c1. +// +// Randomly interspersed partial sampling breaks that guarantee, and rate() +// exacerbates it. Specifically, suppose bucket le=1000 has a count of 10 from +// 4 samples but the bucket with le=2000 has a count of 7 from 3 samples. The +// monotonicity is broken. It is exacerbated by rate() because under normal +// operation, cumulative counting of buckets will cause the bucket counts to +// diverge such that small differences from missing samples are not a problem. +// rate() removes this divergence.) +// +// bucketQuantile depends on that monotonicity to do a binary search for the +// bucket with the φ-quantile count, so breaking the monotonicity +// guarantee causes bucketQuantile() to return undefined (nonsense) results. +// +// As a somewhat hacky solution until ingestion is atomic per scrape, we +// calculate the "envelope" of the histogram buckets, essentially removing +// any decreases in the count between successive buckets. + +func ensureMonotonic(buckets buckets) { + max := buckets[0].count + for i := 1; i < len(buckets); i++ { + switch { + case buckets[i].count > max: + max = buckets[i].count + case buckets[i].count < max: + buckets[i].count = max + } + } +} + +// End of copied code. + +func readLines() []string { + r := bufio.NewReader(os.Stdin) + bytes := []byte{} + lines := []string{} + for { + line, isPrefix, err := r.ReadLine() + if err != nil { + break + } + bytes = append(bytes, line...) + if !isPrefix { + str := strings.TrimSpace(string(bytes)) + if len(str) > 0 { + lines = append(lines, str) + bytes = []byte{} + } + } + } + if len(bytes) > 0 { + lines = append(lines, string(bytes)) + } + return lines +} + +func main() { + lines := readLines() + for _, text := range lines { + // Example input + // "[1, 2, 4, 8, 16]", "[1, 5, 8, 10, 14]", 0.9" + // bounds - counts - quantile + parts := strings.Split(text, "\",") + + var bucketNumbers []float64 + // Strip the ends with square brackets + text = parts[0][2 : len(parts[0])-1] + // Parse the bucket bounds + for _, num := range strings.Split(text, ",") { + num = strings.TrimSpace(num) + number, err := strconv.ParseFloat(num, 64) + if err == nil { + bucketNumbers = append(bucketNumbers, number) + } + } + + var bucketCounts []float64 + // Strip the ends with square brackets + text = parts[1][2 : len(parts[1])-1] + // Parse the bucket counts + for _, num := range strings.Split(text, ",") { + num = strings.TrimSpace(num) + number, err := strconv.ParseFloat(num, 64) + if err == nil { + bucketCounts = append(bucketCounts, number) + } + } + + // Parse the quantile + q, err := strconv.ParseFloat(parts[2], 64) + var b buckets + + if err == nil { + for i := 0; i < len(bucketNumbers); i++ { + b = append(b, bucket{upperBound: bucketNumbers[i], count: bucketCounts[i]}) + } + } + fmt.Println(bucketQuantile(q, b)) + } +} diff --git a/docker-examples/docker-signoz/docker/common/locust-scripts/locustfile.py b/docker-examples/docker-signoz/docker/common/locust-scripts/locustfile.py new file mode 100644 index 0000000..0b51820 --- /dev/null +++ b/docker-examples/docker-signoz/docker/common/locust-scripts/locustfile.py @@ -0,0 +1,16 @@ +from locust import HttpUser, task, between +class UserTasks(HttpUser): + wait_time = between(5, 15) + + @task + def rachel(self): + self.client.get("/dispatch?customer=123&nonse=0.6308392664170006") + @task + def trom(self): + self.client.get("/dispatch?customer=392&nonse=0.015296363321630757") + @task + def japanese(self): + self.client.get("/dispatch?customer=731&nonse=0.8022286220408668") + @task + def coffee(self): + self.client.get("/dispatch?customer=567&nonse=0.0022220379420636593") diff --git a/docker-examples/docker-signoz/docker/common/nginx-config.conf b/docker-examples/docker-signoz/docker/common/nginx-config.conf new file mode 100644 index 0000000..f7943e2 --- /dev/null +++ b/docker-examples/docker-signoz/docker/common/nginx-config.conf @@ -0,0 +1,51 @@ +server { + listen 3301; + server_name _; + + gzip on; + gzip_static on; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_proxied any; + gzip_vary on; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + + # to handle uri issue 414 from nginx + client_max_body_size 24M; + large_client_header_buffers 8 128k; + + location / { + if ( $uri = '/index.html' ) { + add_header Cache-Control no-store always; + } + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + location ~ ^/api/(v1|v3)/logs/(tail|livetail){ + proxy_pass http://query-service:8080; + proxy_http_version 1.1; + + # connection will be closed if no data is read for 600s between successive read operations + proxy_read_timeout 600s; + + # dont buffer the data send it directly to client. + proxy_buffering off; + proxy_cache off; + } + + location /api { + proxy_pass http://query-service:8080/api; + # connection will be closed if no data is read for 600s between successive read operations + proxy_read_timeout 600s; + } + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file diff --git a/dockerfile-config/local/log4rs-config.yml b/dockerfile-config/local/log4rs-config.yml new file mode 100644 index 0000000..a6227e7 --- /dev/null +++ b/dockerfile-config/local/log4rs-config.yml @@ -0,0 +1,53 @@ +refresh_rate: 30 seconds +appenders: + stdout: + kind: console + encoder: + pattern: "{d(%Y-%m-%d %H:%M:%S):<20} {M:>20.30}:{L:>3} {h({l})} {m}\n" + logstash: + kind: logstash + hostname: logstash + port: 50000 + buffer_size: 100 + log_queue_len: 1000 + buffer_lifetime: 1s + extra_fields: + node_id: "5D1fNXzvv5NjV1ysLjirC4WY92RNsVH18vjmcszZd8on" + node_name: "solana-test-validator-logs" + +root: + level: error + appenders: + - stdout + +loggers: + sologger_geyser_plugin::log_processor::error: + level: error + appenders: + - stdout + additive: false + sologger_geyser_plugin::log_processor::info: + level: info + appenders: + - stdout + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::update_account: + level: error + appenders: + - stdout + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::notify_block_metadata: + level: error + appenders: + - stdout + additive: false + sologger_geyser_plugin::geyser_plugin_sologger::update_slot_status: + level: error + appenders: + - stdout + additive: false + sologger_geyser_plugin::geyser_plugin_sologger: + level: info + appenders: + - stdout + additive: false \ No newline at end of file diff --git a/dockerfile-config/local/opentelemetry-config.json b/dockerfile-config/local/opentelemetry-config.json new file mode 100644 index 0000000..949ed68 --- /dev/null +++ b/dockerfile-config/local/opentelemetry-config.json @@ -0,0 +1,9 @@ +{ + "logConfig": { + "service.name": "solana-oltp-logging-service" + }, + "endpoint": "http://otel-collector:4317", + "metricsEndpoint": "http://otel-collector:4318/v1/metrics", + "tracesEndpoint": "http://otel-collector:4318/v1/traces", + "logLevel": "INFO" +} \ No newline at end of file diff --git a/dockerfile-config/local/sologger-config.json b/dockerfile-config/local/sologger-config.json new file mode 100644 index 0000000..835e897 --- /dev/null +++ b/dockerfile-config/local/sologger-config.json @@ -0,0 +1,11 @@ +{ + "log4rsConfigLocation": "../config/local/log4rs-config.yml", + "opentelemetryConfigLocation": "../config/local/opentelemetry-config.json", + "rpcUrl": "wss://api.devnet.solana.com", + "programsSelector" : { + "programs" : ["*"] + }, + "accountDataNotificationsEnabled": false, + "transactionNotificationsEnabled": true, + "logProcessorWorkerThreadCount": 2 +} \ No newline at end of file diff --git a/dockerfile-config/sologger-geyser-plugin-config.json b/dockerfile-config/sologger-geyser-plugin-config.json new file mode 100644 index 0000000..4cc44f6 --- /dev/null +++ b/dockerfile-config/sologger-geyser-plugin-config.json @@ -0,0 +1,3 @@ +{ + "libpath": "/usr/lib/plugin.so" +} \ No newline at end of file diff --git a/scripts/check_errors.sh b/scripts/check_errors.sh new file mode 100644 index 0000000..acbb279 --- /dev/null +++ b/scripts/check_errors.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -ex + +cargo build + RUST_LOG=error solana-test-validator \ + --geyser-plugin-config config/geyser-logstash-plugin-config.json \ + || tail -f test-ledger/validator.log \ No newline at end of file diff --git a/scripts/clean.sh b/scripts/clean.sh new file mode 100644 index 0000000..c42164f --- /dev/null +++ b/scripts/clean.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +rm -rf test-ledger \ No newline at end of file diff --git a/scripts/logs.sh b/scripts/logs.sh new file mode 100644 index 0000000..903ab86 --- /dev/null +++ b/scripts/logs.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +tail -f test-ledger/validator.log diff --git a/scripts/run.sh b/scripts/run.sh new file mode 100755 index 0000000..3137817 --- /dev/null +++ b/scripts/run.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -ex + +cargo build --features 'enable_logstash' + +solana-test-validator --geyser-plugin-config ../config/sologger-geyser-plugin-config.json \ No newline at end of file diff --git a/scripts/run_otel.sh b/scripts/run_otel.sh new file mode 100755 index 0000000..ed86e5d --- /dev/null +++ b/scripts/run_otel.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -ex + +cargo build --features 'enable_otel' + +solana-test-validator --geyser-plugin-config ../config/sologger-geyser-plugin-config.json \ No newline at end of file diff --git a/src/config_loader.rs b/src/config_loader.rs new file mode 100644 index 0000000..1ee530a --- /dev/null +++ b/src/config_loader.rs @@ -0,0 +1,82 @@ +use std::env; +use std::fs::File; +use std::io::Read; +use std::path::Path; +use log::trace; +use anyhow::Result; +use sologger_log_context::programs_selector::ProgramsSelector; +use crate::sologger_config::SologgerConfig; + +const DEFAULT_CONFIG_PATH: &str = "/config/local/sologger-config.json"; +const DEFAULT_DIR: &str = "/"; + +pub(crate) fn load_config() -> Result<(SologgerConfig, ProgramsSelector)> { + let default_config = get_default_config(); + let sologger_config_path = env::var("SOLOGGER_APP_CONFIG_LOC").unwrap_or(default_config.to_string()); + + trace!("sologger_config_path: {}", sologger_config_path); + let mut file = File::open(Path::new(sologger_config_path.as_str()))?; + let mut contents = String::new(); + file.read_to_string(&mut contents) + .expect("Failed to read contents of sologger-config.json"); + + let result: serde_json::Value = serde_json::from_str(&contents).unwrap(); + trace!("SologgerConfig: {}", result.to_string()); + let programs_selector = create_programs_selector_from_config(&result); + let sologger_config = serde_json::from_str(&contents).map_err(|_err| ConfigError::Loading)?; + + Ok((sologger_config, programs_selector)) +} + +fn get_default_config() -> String { + let parent_dir = get_parent_dir(); + format!("{}{}", parent_dir, DEFAULT_CONFIG_PATH) +} + +fn get_parent_dir() -> String { + match env::current_dir() { + Ok(path_buf) => match path_buf.parent() { + Some(path) => path.display().to_string(), + None => String::from(DEFAULT_DIR), + }, + Err(_) => String::from(DEFAULT_DIR), + } +} + +fn create_programs_selector_from_config(config: &serde_json::Value) -> ProgramsSelector { + let programs_selector = &config["programsSelector"]; + + if programs_selector.is_null() { + ProgramsSelector::default() + } else { + let programs = &programs_selector["programs"]; + let programs: Vec = if programs.is_array() { + programs + .as_array() + .unwrap() + .iter() + .map(|val| val.as_str().unwrap().to_string()) + .collect() + } else { + Vec::default() + }; + + ProgramsSelector::new(&programs) + } +} + +#[derive(Debug)] +enum ConfigError { + Loading, +} + +impl std::error::Error for ConfigError {} + +impl std::fmt::Display for ConfigError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use ConfigError::*; + match self { + Loading => write!(f, "Loading"), + } + } +} \ No newline at end of file diff --git a/src/entrypoint.rs b/src/entrypoint.rs new file mode 100644 index 0000000..3c1e3df --- /dev/null +++ b/src/entrypoint.rs @@ -0,0 +1,18 @@ +use { + crate::geyser_plugin_sologger::SologgerGeyserPlugin, + solana_geyser_plugin_interface::geyser_plugin_interface::GeyserPlugin, +}; + +use crate::geyser_plugin_sologger::PluginContext; + +#[no_mangle] +#[allow(improper_ctypes_definitions)] +/// # Safety +/// +/// This function simply allocates a GeyserPluginHook, +/// and returns a pointer to it as trait GeyserPlugin. +pub unsafe extern "C" fn _create_plugin() -> *mut dyn GeyserPlugin { + let plugin = SologgerGeyserPlugin { context: PluginContext::default() }; + let plugin: Box = Box::new(plugin); + Box::into_raw(plugin) +} \ No newline at end of file diff --git a/src/geyser_plugin_sologger.rs b/src/geyser_plugin_sologger.rs new file mode 100644 index 0000000..8a66e45 --- /dev/null +++ b/src/geyser_plugin_sologger.rs @@ -0,0 +1,358 @@ +use std::{env, fs::File, io::Read, thread}; +use std::path::Path; +use std::sync::{Arc, Mutex}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread::JoinHandle; +use std::time::Duration; + +use anyhow::Result as AnyResult; +use crossbeam_deque::{Injector, Steal, Worker}; +use log::{error, info, trace}; +use solana_geyser_plugin_interface::geyser_plugin_interface::{ReplicaAccountInfoV2, ReplicaTransactionInfoV2}; +use solana_transaction_status::option_serializer::OptionSerializer; +use sologger_log_context::programs_selector::ProgramsSelector; +use sologger_log_context::sologger_log_context::LogContext; + +use { + solana_geyser_plugin_interface::geyser_plugin_interface::{ + GeyserPlugin, GeyserPluginError, + ReplicaAccountInfoVersions, ReplicaBlockInfoVersions, ReplicaTransactionInfoVersions, + SlotStatus, + }, + std::fmt::{ + Debug, Formatter, + }, +}; + +use crate::config_loader; +use crate::geyser_plugin_sologger_config::GeyserPluginSologgerConfig; +use crate::inner_transaction::ReplicaTransactionInfo; +use crate::log_processor::{from_rpc_response, log_contexts_from_logs}; +use crate::logger_lib::init_logger; +use crate::sologger_config::SologgerConfig; + +pub struct PluginContext { + pub(crate) programs_selector: ProgramsSelector, + pub(crate) injector: Arc>, + pub sologger_config: SologgerConfig, + pub running: Arc, + pub handles: Arc>>>, +} + +impl PluginContext { + pub(crate) fn default() -> PluginContext { + PluginContext { + programs_selector: ProgramsSelector::default(), + injector: Arc::new(Default::default()), + sologger_config: SologgerConfig::default(), + running: Arc::new(Default::default()), + handles: Arc::new(Mutex::new(Vec::new())) + } + } + + pub fn join_threads(&self) { + let handles = { + let mut handles = self.handles.lock().unwrap(); + std::mem::take(&mut *handles) // empty the Vec inside the Mutex + }; + for handle in handles { + handle.join().unwrap(); + } + } +} + +/// This is the main object returned by our dynamic library in entrypoint.rs +pub struct SologgerGeyserPlugin { + pub context: PluginContext +} + +pub struct Task { + slot: u64, + transaction_info: Option, + programs_select: ProgramsSelector, +} + +/// Implementation of GeyserPlugin trait/interface +/// https://docs.rs/solana-geyser-plugin-interface/latest/solana_geyser_plugin_interface/geyser_plugin_interface/trait.GeyserPlugin.html +impl GeyserPlugin for SologgerGeyserPlugin { + fn name(&self) -> &'static str { + "GeyserPluginHook" + } + + /// Lifecycle: the plugin has been loaded by the system + /// used for doing whatever initialization is required by the plugin. + /// The _config_file contains the name of the + /// of the config file. The config must be in JSON format and + /// include a field "libpath" indicating the full path + /// name of the shared library implementing this interface. + fn on_load(&mut self, _config_file: &str) -> solana_geyser_plugin_interface::geyser_plugin_interface::Result<()> { + let (sologger_config, program_selector) = config_loader::load_config().expect("Error loading sologger config"); + let config = sologger_config.clone(); + self.context.sologger_config = sologger_config; + self.context.programs_selector = program_selector; + + let _logger = init_logger(&config); + + info!("Programs Selected: {:?}", &self.context.programs_selector); + + // Create an atomic flag for shutdown signal + let running = Arc::new(AtomicBool::new(true)); + let running_clone = Arc::clone(&running); + self.context.running = Arc::clone(&running_clone); + + // Create an injector for task management + self.context.injector = Arc::new(Injector::new()); + + // Start worker threads for processing tasks + let thread_count = config.log_processor_worker_thread_count as usize; + info!("Starting {:?} log processing worker threads...", &thread_count); + for _ in 0..thread_count { + let injector_clone = Arc::clone(&self.context.injector); + let running_worker = Arc::clone(&running_clone); + let handle = thread::spawn(move || { + Self::worker_thread(injector_clone, running_worker); + }); + self.context.handles.lock().unwrap().push(handle); + } + + info!("sologger-geyser-plugin loaded"); + + Ok(()) + } + + /// Lifecycle: the plugin will be unloaded by the plugin manager + /// Note: Do any cleanup necessary. + fn on_unload(&mut self) { + info!("[on_unload] - Flushing logger"); + let _ = &self.context.running.store(false, Ordering::SeqCst); + self.context.join_threads(); + log::logger().flush(); + } + + /// Event: an account has been updated at slot + /// - When `is_startup` is true, it indicates the account is loaded from + /// snapshots when the validator starts up. + /// - When `is_startup` is false, the account is updated during transaction processing. + /// Note: The account is versioned, so you can decide how to handle the different + /// implementations. + fn update_account(&self, account: ReplicaAccountInfoVersions, slot: u64, _is_startup: bool) -> solana_geyser_plugin_interface::geyser_plugin_interface::Result<()> { + match account { + ReplicaAccountInfoVersions::V0_0_1(_) => { + return Err(GeyserPluginError::AccountsUpdateError { msg: "ReplicaAccountInfoVersions::V0_0_1 it not supported".to_string() }); + } + ReplicaAccountInfoVersions::V0_0_2(account) => { + let _acc = format!( + "V0_0_2 pubkey: {}, owner: {}", + bs58::encode(account.pubkey).into_string(), + bs58::encode(account.owner).into_string(), + ); + info!(target: "sologger_geyser_plugin::geyser_plugin_sologger::update_account", "[update_account] - account: {:#?}, slot:{:#?}, is_startup:{:#?}", _acc, slot, _is_startup); + } + ReplicaAccountInfoVersions::V0_0_3(account) => { + let _acc = format!( + "Updated V0_0_3 account pubkey: {}, owner: {}", + bs58::encode(account.pubkey).into_string(), + bs58::encode(account.owner).into_string(), + ); + info!(target: "sologger_geyser_plugin::geyser_plugin_sologger::update_account", "[update_account] - account: {:#?}, slot:{:#?}, is_startup:{:#?}", _acc, slot, _is_startup); + } + } + Ok(()) + } + + // Lifecycle: called when all accounts have been notified when the validator + // restores the AccountsDb from snapshots at startup. + fn notify_end_of_startup(&self) -> solana_geyser_plugin_interface::geyser_plugin_interface::Result<()> { + info!("[notify_end_of_startup]"); + Ok(()) + } + + // Event: a slot status is updated. + fn update_slot_status(&self, _slot: u64, _parent: Option, _status: SlotStatus) -> solana_geyser_plugin_interface::geyser_plugin_interface::Result<()> { + info!(target: "sologger_geyser_plugin::geyser_plugin_sologger::update_slot_status", "[update_slot_status], slot:{:#?}, parent:{:#?}, status:{:#?}", _slot, _parent, _status); + Ok(()) + } + + /// Event: a transaction is updated at a slot. + #[allow(unused_variables)] + fn notify_transaction(&self, transaction: ReplicaTransactionInfoVersions, slot: u64) -> solana_geyser_plugin_interface::geyser_plugin_interface::Result<()> { + let replication_transaction_info = ReplicaTransactionInfo::from((transaction, slot)); + let task = Task { + slot, + transaction_info: Some(replication_transaction_info), + programs_select: self.context.programs_selector.clone(), + }; + self.context.injector.push(task); + + Ok(()) + } + + fn notify_block_metadata(&self, blockinfo: ReplicaBlockInfoVersions) -> solana_geyser_plugin_interface::geyser_plugin_interface::Result<()> { + match blockinfo { + ReplicaBlockInfoVersions::V0_0_1(_blockinfo) => { + info!(target: "sologger_geyser_plugin::geyser_plugin_sologger::notify_block_metadata", "[notify_block_metadata], block_info:{:#?}", _blockinfo); + } + ReplicaBlockInfoVersions::V0_0_2(_blockinfo) => { + info!(target: "sologger_geyser_plugin::geyser_plugin_sologger::notify_block_metadata", "[notify_block_metadata], block_info:{:#?}", _blockinfo); + } + ReplicaBlockInfoVersions::V0_0_3(_blockinfo) => { + info!(target: "sologger_geyser_plugin::geyser_plugin_sologger::notify_block_metadata", "[notify_block_metadata], block_info:{:#?}", _blockinfo); + } + } + Ok(()) + } + + fn account_data_notifications_enabled(&self) -> bool { + trace!("[account_data_notifications_enabled] - are account data notifications enabled: true"); + self.context.sologger_config.account_data_notifications_enabled + } + + fn transaction_notifications_enabled(&self) -> bool { + trace!("[transaction_notifications_enabled] - are transaction notifications enabled: true"); + self.context.sologger_config.transaction_notifications_enabled + } +} + +impl SologgerGeyserPlugin { + fn worker_thread(injector: Arc>, running: Arc) { + // Create a worker deque for this thread + let worker = Worker::new_fifo(); + let stealer = worker.stealer(); + info!("Worker thread started"); + + while running.load(Ordering::SeqCst) { + if let Some(task) = worker.pop() { + Self::process_task(task); + } else if let Steal::Success(task) = injector.steal() { + Self::process_task(task); + } else if let Steal::Success(task) = stealer.steal() { + Self::process_task(task); + } else { + // Sleep for a short duration to avoid busy-waiting + thread::sleep(Duration::from_millis(10)); + } + } + info!("Worker thread shutting down"); + } + + fn process_task(task: Task) { + // Process the task (example: log the slot number) + if let Some(transaction_info) = task.transaction_info { + trace!("Processing transaction info at slot: {}", task.slot); + // Process the transaction info here + match transaction_info.meta.log_messages { + OptionSerializer::Some(ref log_messages) => { + if log_messages.is_empty() { + return; + } + let log_context_result = from_rpc_response(&transaction_info, task.programs_select, task.slot); + match log_context_result { + Ok(log_contexts) => { + log_contexts_from_logs(&log_contexts).expect("Error logging log contexts"); + } + Err(_) => { error!("Error occurred logging the log contexts") } + } + } + OptionSerializer::None => {} + OptionSerializer::Skip => {} + } + } + } +} + +/// Also required by GeyserPlugin trait +impl Debug for SologgerGeyserPlugin { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "GeyserPluginHook") + } +} + +#[cfg(test)] +pub(crate) mod tests { + use solana_transaction_status::TransactionStatusMeta; + + use { + solana_sdk::{ + hash::Hash, + signature::{Keypair, Signature, Signer}, + system_transaction, + transaction::{ + SanitizedTransaction, SimpleAddressLoader, Transaction, VersionedTransaction, + }, + }, + super::*, + }; + + fn build_test_transaction_legacy() -> Transaction { + let keypair1 = Keypair::new(); + let pubkey1 = keypair1.pubkey(); + let zero = Hash::default(); + system_transaction::transfer(&keypair1, &pubkey1, 42, zero) + } + + #[test] + fn notify_transaction_test() { + solana_logger::setup_with_default("info"); + + let mut logs: Vec = vec![]; + logs.push("Program 9RX7oz3WN5VRTqekBBHBvEJFVMNRnrCmVy7S6B6S5oU7 invoke [1]".to_string()); + logs.push("Program log: Instruction: Initialize".to_string()); + logs.push("Program 11111111111111111111111111111111 invoke [2]".to_string()); + logs.push("Program 11111111111111111111111111111111 success".to_string()); + logs.push("Program log: Initialized new event. Current value".to_string()); + logs.push("Program 9RX7oz3WN5VRTqekBBHBvEJFVMNRnrCmVy7S6B6S5oU7 consumed 59783 of 200000 compute units".to_string()); + logs.push("Program 9RX7oz3WN5VRTqekBBHBvEJFVMNRnrCmVy7S6B6S5oU7 success".to_string()); + + let signature = Signature::from([1u8; 64]); + + let message_hash = Hash::new_unique(); + let transaction = build_test_transaction_legacy(); + + let transaction = VersionedTransaction::from(transaction); + + let transaction = SanitizedTransaction::try_create( + transaction, + message_hash, + Some(true), + SimpleAddressLoader::Disabled, + ) + .unwrap(); + + let transaction_status_meta = TransactionStatusMeta { + status: Ok(()), + fee: 0, + pre_balances: vec![], + post_balances: vec![], + inner_instructions: None, + log_messages: Option::from(logs), + pre_token_balances: None, + post_token_balances: None, + rewards: None, + loaded_addresses: Default::default(), + return_data: None, + compute_units_consumed: None, + }; + + let transaction_info_v2 = ReplicaTransactionInfoV2 { + signature: &Default::default(), + is_vote: false, + transaction: &(transaction), + transaction_status_meta: &transaction_status_meta, + index: 0, + }; + + let programs_selector = ProgramsSelector::new(&["9RX7oz3WN5VRTqekBBHBvEJFVMNRnrCmVy7S6B6S5oU7".to_string()]); + let mut geyser_logstash_plugin = SologgerGeyserPlugin { + context: PluginContext { + programs_selector, + injector: Arc::new(Default::default()), + sologger_config: Default::default(), + running: Arc::new(Default::default()), + handles: Arc::new(Mutex::new(vec![])), + }, + }; + + ReplicaTransactionInfoVersions::V0_0_2(&transaction_info_v2); + let _ = SologgerGeyserPlugin::notify_transaction(&mut geyser_logstash_plugin, ReplicaTransactionInfoVersions::V0_0_2(&transaction_info_v2), 1u64); + } +} diff --git a/src/geyser_plugin_sologger_config.rs b/src/geyser_plugin_sologger_config.rs new file mode 100644 index 0000000..82f768e --- /dev/null +++ b/src/geyser_plugin_sologger_config.rs @@ -0,0 +1,10 @@ +use serde::{ + Deserialize, + Serialize +}; + +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] +pub struct GeyserPluginSologgerConfig { + pub(crate) logstash_config_location: String, + pub(crate) log_geyser_functions: bool +} \ No newline at end of file diff --git a/src/inner_transaction.rs b/src/inner_transaction.rs new file mode 100644 index 0000000..02e9394 --- /dev/null +++ b/src/inner_transaction.rs @@ -0,0 +1,35 @@ +use solana_geyser_plugin_interface::geyser_plugin_interface::ReplicaTransactionInfoVersions; +use solana_sdk::clock::UnixTimestamp; +use solana_sdk::signature::Signature; +use solana_sdk::transaction::SanitizedTransaction; +use solana_transaction_status::UiTransactionStatusMeta; + +#[derive(Debug, Clone)] +pub struct ReplicaTransactionInfo { + pub signature: Signature, + pub is_vote: bool, + pub transaction: SanitizedTransaction, + pub meta: UiTransactionStatusMeta, + pub slot: u64, + pub block_time: Option, + pub index: usize, +} + +impl<'a> From<(ReplicaTransactionInfoVersions<'a>, u64)> for ReplicaTransactionInfo { + fn from((transaction, slot): (ReplicaTransactionInfoVersions<'a>, u64)) -> Self { + match transaction { + ReplicaTransactionInfoVersions::V0_0_1(_transaction) => { + unreachable!("ReplicaTransactionInfoVersions::V0_0_1 is not supported") + } + ReplicaTransactionInfoVersions::V0_0_2(transaction) => Self { + signature: *transaction.signature, + is_vote: transaction.is_vote, + transaction: transaction.transaction.clone(), + meta: transaction.transaction_status_meta.clone().into(), + slot, + block_time: None, + index: transaction.index, + }, + } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..da920e3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,9 @@ + +pub mod geyser_plugin_sologger; +mod entrypoint; +mod geyser_plugin_sologger_config; +mod logger_lib; +mod sologger_config; +mod inner_transaction; +mod log_processor; +mod config_loader; diff --git a/src/log_processor.rs b/src/log_processor.rs new file mode 100644 index 0000000..dfd575b --- /dev/null +++ b/src/log_processor.rs @@ -0,0 +1,43 @@ +use anyhow::Result; +use log::{error, info}; +use solana_geyser_plugin_interface::geyser_plugin_interface::ReplicaTransactionInfoV2; +use solana_transaction_status::option_serializer::OptionSerializer; +use sologger_log_context::programs_selector::ProgramsSelector; +use sologger_log_context::sologger_log_context::LogContext; + +use crate::inner_transaction::ReplicaTransactionInfo; + +pub fn from_rpc_response( + transaction: &ReplicaTransactionInfo, + program_selector: ProgramsSelector, + slot: u64 +) -> Result> { + let log_contexts = match &transaction.meta.log_messages { + OptionSerializer::Some(logs) => { + let transaction_error = transaction.meta.status.clone().err() + .map_or_else(|| "".to_string(), |err| err.to_string()); + let sig = transaction.signature.to_string(); + LogContext::parse_logs( + &logs, + transaction_error, + &program_selector, + slot, + sig, + ) + } + OptionSerializer::None | OptionSerializer::Skip => Vec::new(), + }; + Ok(log_contexts) +} + + +pub fn log_contexts_from_logs(log_contexts: &Vec) -> Result<()> { + for log_context in log_contexts { + if log_context.has_errors() { + error!(target: "sologger_geyser_plugin::log_processor::error", "{}", &log_context.to_json()); + } else { + info!(target: "sologger_geyser_plugin::log_processor::info", "{}", &log_context.to_json()); + } + } + Ok(()) +} \ No newline at end of file diff --git a/src/logger_lib.rs b/src/logger_lib.rs new file mode 100644 index 0000000..dcc9769 --- /dev/null +++ b/src/logger_lib.rs @@ -0,0 +1,89 @@ +#![cfg_attr(debug_assertions, allow(dead_code, unused_imports))] +use std::path::Path; +use log4rs::init_file; +use log::{debug, error}; + +use crate::sologger_config::SologgerConfig; + +pub fn init_logger(sologger_config: &SologgerConfig) { + #[cfg(feature = "enable_logstash")] + init_logger_logstash(sologger_config); + #[cfg(feature = "enable_otel")] + init_log4rs(&sologger_config.log4rs_config_location).expect("Error initializing log4rs for enable_otel feature"); + #[cfg(feature = "enable_otel")] + init_logger_otel(sologger_config); +} + +#[cfg(feature = "enable_logstash")] +pub fn init_logger_logstash(sologger_config: &SologgerConfig) { + if !Path::new(&sologger_config.log4rs_config_location).exists() { + panic!("Log4rs config file not found"); + }; + sologger_log_transport::logstash_lib::init_logstash_logger( + &sologger_config.log4rs_config_location, + ) + .expect("Logger not initialized"); +} + +#[cfg(feature = "enable_otel")] +pub fn init_logger_otel(sologger_config: &SologgerConfig) { + + let rt = tokio::runtime::Runtime::new().unwrap(); + if !Path::new(&sologger_config.opentelemetry_config_location).exists() { + panic!("OTel config file not found {}", &sologger_config.opentelemetry_config_location); + }; + + let config = sologger_config.clone(); + rt.spawn(async move { + println!("Starting OTel in Tokio RT"); + + let config = sologger_log_transport::opentelemetry_lib::get_otel_config(&config.opentelemetry_config_location); + let _ = sologger_log_transport::opentelemetry_lib::init_logs_opentelemetry(&config); + }); +} + +pub fn init_log4rs(log4rs_config_location: &String) -> anyhow::Result<()> { + match { + init_file( + log4rs_config_location, + log4rs::config::Deserializers::default(), + ) + } { + Ok(_) => { + debug!("Logger initialized with logstash successfully") + } + Err(err) => { + error!("init_logstash_logger not initialized! {}", err.to_string()) + } + }; + Ok(()) +} + + +#[cfg(test)] +mod tests { + use crate::logger_lib::init_logger; + use crate::sologger_config::SologgerConfig; + use serde_json::json; + + #[test] + pub fn init_logger_test() { + //TODO fix for config location + let config = json!( + { + "log4rsConfigLocation": "./config/log4rs-config.yml", + "rpcUrl": "wss://api.mainnet-beta.solana.com", + "programsSelector" : { + "programs" : ["*"] + }, + "accountDataNotificationsEnabled": false, + "transactionNotificationsEnabled": true, + "logProcessorWorkerThreadCount": 1 + + } + ); + + let sologger_config = serde_json::from_value::(config).unwrap(); + init_logger(&sologger_config); + } +} diff --git a/src/sologger_config.rs b/src/sologger_config.rs new file mode 100644 index 0000000..8794ea9 --- /dev/null +++ b/src/sologger_config.rs @@ -0,0 +1,95 @@ +use serde::{Deserialize, Serialize}; +use serde_json::json; + +/// This is the main configuration file for sologger. The location of this file is specified by the `SOLOGGER_APP_CONFIG_LOC` environment variable or as the first argument via the cargo run command. +#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq, Debug)] +#[serde(rename_all = "camelCase")] +pub struct SologgerConfig { + /// The location of the log4rs config file + #[serde(default)] + pub log4rs_config_location: String, + /// The location of the opentelemetry config file + #[serde(default)] + pub opentelemetry_config_location: String, + /// The URL of the RPC endpoint to connect to + pub rpc_url: String, + /// The measure of the network confirmation and stake levels on a particular block. + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + pub commitment_level: Option, + /// Set to true to subscribe to all transactions, including simple vote transactions. Otherwise, subscribe to all transactions except for simple vote transactions + #[serde(default)] + pub all_with_votes: bool, + /// Determines if account data notifications are enabled. + pub account_data_notifications_enabled: bool, + /// Determines whether transaction notifications are enabled or not + pub transaction_notifications_enabled: bool, + /// The number of worker threads for log processing. These threads are used for parsing the unstructured logs into structured logs and sending them to the logger. The number needed depend on your validator and use case. + pub log_processor_worker_thread_count: u8 +} + +#[test] +pub fn test_default() { + let config = SologgerConfig::default(); + assert_eq!(config.opentelemetry_config_location, ""); +} + +#[test] +pub fn test_deserialize() { + let config = json!( + { + "log4rsConfigLocation": "./config/log4rs-config.yml", + "rpcUrl": "wss://api.mainnet-beta.solana.com", + "programsSelector" : { + "programs" : ["*"] + }, + "accountDataNotificationsEnabled": false, + "transactionNotificationsEnabled": true, + "logProcessorWorkerThreadCount": 1 + } + ); + + let sologger_config = serde_json::from_value::(config).unwrap(); + assert_eq!(sologger_config.rpc_url, "wss://api.mainnet-beta.solana.com"); + assert_eq!( + sologger_config.log4rs_config_location, + "./config/log4rs-config.yml" + ); + assert_eq!(sologger_config.all_with_votes, false); + assert_eq!(sologger_config.commitment_level, None); +} + +#[test] +pub fn test_deserialize_all() { + let config = json!( + { + "log4rsConfigLocation": "./config/log4rs-config.yml", + "opentelemetryConfigLocation": "./config/opentelemetry-config.json", + "rpcUrl": "wss://api.mainnet-beta.solana.com", + "programsSelector" : { + "programs" : ["*"] + }, + "allWithVotes": true, + "commitmentLevel": "recent", + "accountDataNotificationsEnabled": true, + "transactionNotificationsEnabled": true, + "logProcessorWorkerThreadCount": 2 + } + ); + + let sologger_config = serde_json::from_value::(config).unwrap(); + assert_eq!(sologger_config.rpc_url, "wss://api.mainnet-beta.solana.com"); + assert_eq!( + sologger_config.log4rs_config_location, + "./config/log4rs-config.yml" + ); + assert_eq!( + sologger_config.opentelemetry_config_location, + "./config/opentelemetry-config.json" + ); + assert_eq!(sologger_config.all_with_votes, true); + assert_eq!(sologger_config.commitment_level.unwrap(), "recent"); + assert_eq!(sologger_config.account_data_notifications_enabled, true); + assert_eq!(sologger_config.transaction_notifications_enabled, true); + assert_eq!(sologger_config.log_processor_worker_thread_count, 2); +}