{"id":20564328,"url":"https://github.com/tarantool/tracing","last_synced_at":"2026-03-03T07:41:41.806Z","repository":{"id":45078566,"uuid":"219976544","full_name":"tarantool/tracing","owner":"tarantool","description":"OpenTracing API for Tarantool","archived":false,"fork":false,"pushed_at":"2024-10-25T12:35:22.000Z","size":230,"stargazers_count":9,"open_issues_count":3,"forks_count":2,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-03-28T04:03:32.177Z","etag":null,"topics":["lua","opentracing","tarantool","zipkin"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tarantool.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-06T11:01:18.000Z","updated_at":"2024-10-25T12:35:29.000Z","dependencies_parsed_at":"2024-10-24T19:37:09.078Z","dependency_job_id":"ea4d6d29-0721-4388-b0d8-90bb1d9faa72","html_url":"https://github.com/tarantool/tracing","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Ftracing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Ftracing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Ftracing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Ftracing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tarantool","download_url":"https://codeload.github.com/tarantool/tracing/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248904640,"owners_count":21180835,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lua","opentracing","tarantool","zipkin"],"created_at":"2024-11-16T04:25:42.674Z","updated_at":"2026-03-03T07:41:36.785Z","avatar_url":"https://github.com/tarantool.png","language":"Lua","readme":"# Tracing for Tarantool\n\n![tracing-img](https://user-images.githubusercontent.com/8830475/68295738-17abe800-00a4-11ea-855f-d46589b89bed.png)\n\n`Tracing` module for Tarantool includes the following parts:\n\n* OpenTracing API\n* Zipkin tracer\n\n## Table of contents\n\n* [OpenTracing](#opentracing)\n    * [Required Reading](#required-reading)\n    * [Conventions](#conventions)\n    * [Span](#span)\n    * [SpanContext](#spancontext)\n    * [Tracer](#tracer)\n    * [Basic usage](#basic-usage)\n* [Zipkin](#zipkin)\n    * [Basic usage](#basic-usage)\n* [Examples](#examples)\n    * [HTTP](#http)\n    * [Cartridge](#tarantool-cartridge)\n\n## OpenTracing\n\nThis library is a Tarantool platform API for OpenTracing.\n\n### Required Reading\n\nTo fully understand this platform API,\nit's helpful to be familiar with the [OpenTracing project](https://opentracing.io) and\n[terminology](https://opentracing.io/specification/) more specifically.\n\n### Conventions\n\n  - All timestamps are in microseconds\n\n### Span\n\n\u003e The “span” is the primary building block of a distributed trace,\nrepresenting an individual unit of work done in a distributed system.\nTraces in OpenTracing are defined implicitly by their Spans.\nIn particular, a Trace can be thought of as a directed acyclic graph\n(DAG) of Spans, where the edges between Spans are called References.\n\n\n```lua\nlocal opentracing_span = require('opentracing.span')\n-- tracer - External tracer\n-- context - Span context\n-- name - Name of span\n-- start_timestamp (optional) - Time of span's start in microseconds (by default current time)\nlocal span = opentracing_span.new(tracer, context, name, start_timestamp)\n```\n\n### SpanContext\n\n\u003e The SpanContext carries data across process boundaries.\n\n```lua\nlocal opentracing_span_context = require('opentracing.span_context')\n-- trace_id (optional) - Trace ID (by default generates automatically)\n-- span_id (optional) - Span ID (by default generates automatically)\n-- parent_id (optional) - Span ID of parent span (by default is empty)\n-- should_sample (optional) - Flag is enable collecting data of this span (by default false)\n-- baggage (optional) - Table with trace baggage (by default is empty table)\nlocal context = opentracing_span_context.new({\n                    tracer_id = trace_id,\n                    span_id = span_id,\n                    parent_id = parent_id,\n                    should_sample = should_sample,\n                    baggage = baggage,\n                })\n```\n\n### Tracer\n\n\u003e The Tracer interface creates Spans and understands\nhow to Inject (serialize) and Extract (deserialize)\ntheir metadata across process boundaries.\n\nAn interface for custom tracers\n```lua\nlocal opentracing_tracer = require('opentracing.tracer')\n-- reporter (optional) - Table with `report` method to process finished spans (by default no-op table)\n-- sampler (optional) - Table with `sample` method to select traces to send to distributing tracing system (by default random selection)\n-- But you can implement your own sampler with appropriate sampling strategy\n-- For more information see: https://www.jaegertracing.io/docs/1.11/sampling/\nlocal tracer = opentracing_tracer.new(reporter, sampler)\n```\n\n### Basic usage\n```lua\nlocal zipkin = require('zipkin.tracer')\nlocal opentracing = require('opentracing')\n\n-- Create client to Zipkin and set it global for easy access from any part of app\nlocal tracer = zipkin.new(config)\nopentracing.set_global_tracer(tracer)\n\n-- Create and manage spans manually\nlocal span = opentracing.start_span('root span')\n-- ... your code ...\nspan:finish()\n\n-- Simple wrappers via user's function\n\n-- Creates span before function call and finishes it after\nlocal result = opentracing.trace('one span', func, ...)\n\n-- Wrappers with context passing\nlocal span = opentracing.start_span('root span')\n\n-- Pass your function as third argument and then its arguments\nopentracing.trace_with_context('child span 1', span:context(), func1, ...)\nopentracing.trace_with_context('child span 2', span:context(), func2, ...)\nspan:finish()\n```\n\n## Zipkin\n\n[Zipkin](https://zipkin.io/) is a distributed tracing system.\n\nIt helps gather timing data needed to troubleshoot latency problems in microservice architectures.\nIt manages both the collection and lookup of this data.\n\nThis module allows you to instance Zipkin Tracer that can start spans and\nwill report collected spans to Zipkin Server.\n\n### Basic usage\n\n```lua\nlocal zipkin = require('zipkin.tracer')\n-- First argument is config that contains url of Zipkin API,\n--  method to send collected traces and interval of reports in seconds\n-- Second optional argument is Sampler (see OpenTracing API description), by default random sampler\nlocal tracer = zipkin.new({\n    base_url = 'localhost:9411/api/v2/spans',\n    api_method = 'POST',\n    report_interval = 0,\n}, Sampler)\n\nlocal span = tracer:start_span('example')\n-- ...\nspan:finish()\n```\n\n## Examples\n\n### HTTP\n\n![http-example-img](https://user-images.githubusercontent.com/8830475/68296113-de27ac80-00a4-11ea-844a-20f798d3f5d8.png)\n\nThis example is a Lua port of\n[Go OpenTracing tutorial](https://github.com/yurishkuro/opentracing-tutorial/tree/master/go).\n\n*Complete source code see [here](/examples/http)*\n\n#### Description\n\nThe example demonstrates trace propagation through two services:\n`formatter` that formats the source string to \"Hello, world\"\nand `publisher` that prints it in the console.\n\nAdd data to these services via HTTP; initially it sends `client`.\n\n*Note: example requires http rock (version \u003e= 1.2.0)*\n*Install it using `tt rocks install http 1.2.0`*\n\n#### How to run\n\n* Create `docker-compose.zipkin.yml`\n\n```yaml\n---\nversion: '3.5'\n\n# Initially got from https://github.com/openzipkin/docker-zipkin/blob/master/docker-compose.yml\n\nservices:\n  storage:\n    image: openzipkin/zipkin-mysql:1\n    container_name: mysql\n    networks:\n      - zipkin\n    ports:\n      - 3306:3306\n\n  # The zipkin process services the UI, and also exposes a POST endpoint that\n  # instrumentation can send trace data to. Scribe is disabled by default.\n  zipkin:\n    image: openzipkin/zipkin:1\n    container_name: zipkin\n    networks:\n      - zipkin\n    # Environment settings are defined here https://github.com/openzipkin/zipkin/tree/1.19.0/zipkin-server#environment-variables\n    environment:\n      - STORAGE_TYPE=mysql\n      # Point the zipkin at the storage backend\n      - MYSQL_HOST=mysql\n      # Enable debug logging\n      - JAVA_OPTS=-Dlogging.level.zipkin=DEBUG -Dlogging.level.zipkin2=DEBUG\n    ports:\n      # Port used for the Zipkin UI and HTTP Api\n      - 9411:9411\n    depends_on:\n      - storage\n\n  # Adds a cron to process spans since midnight every hour, and all spans each day\n  # This data is served by http://192.168.99.100:8080/dependency\n  #\n  # For more details, see https://github.com/openzipkin/docker-zipkin-dependencies\n  dependencies:\n    image: openzipkin/zipkin-dependencies:1\n    container_name: dependencies\n    entrypoint: crond -f\n    networks:\n      - zipkin\n    environment:\n      - STORAGE_TYPE=mysql\n      - MYSQL_HOST=mysql\n      # Add the baked-in username and password for the zipkin-mysql image\n      - MYSQL_USER=zipkin\n      - MYSQL_PASS=zipkin\n      # Dependency processing logs\n      - ZIPKIN_LOG_LEVEL=DEBUG\n    depends_on:\n      - storage\n\nnetworks:\n  zipkin:\n```\n\n* Start Zipkin `docker-compose -f docker-compose.zipkin.yml up`\n\n* Run mock applications from separate consoles: consumer, formatter and client\n\nFormatter HTTP server\n```lua\n#!/usr/bin/env tarantool\n\nlocal http_server = require('http.server')\nlocal fiber = require('fiber')\nlocal log = require('log')\nlocal zipkin = require('zipkin.tracer')\nlocal opentracing = require('opentracing')\n\nlocal app = {}\n\nlocal Sampler = {\n    sample = function() return true end,\n}\n\nlocal HOST = '0.0.0.0'\nlocal PORT = '33302'\n\nlocal function handler(req)\n    -- Extract content from request's http headers\n    local ctx, err = opentracing.http_extract(req.headers)\n    if ctx == nil then\n        local resp = req:render({ text = err })\n        resp.status = 400\n        return resp\n    end\n\n    local hello_to = req:query_param('helloto')\n    -- Start new child span\n    local span = opentracing.start_span_from_context(ctx, 'format_string')\n    -- Set service type\n    span:set_component('formatter')\n    span:set_server_kind()\n    span:set_http_method(req.method)\n    span:set_http_path(req.path)\n    local greeting = span:get_baggage_item('greeting')\n    local result = ('%s, %s!'):format(greeting, hello_to)\n    local resp = req:render({ text = result })\n\n    -- Simulate long request processing\n    fiber.sleep(2)\n    span:log_kv({\n        event = 'String format',\n        value = result,\n    })\n    resp.status = 200\n    span:set_http_status_code(resp.status)\n    span:finish()\n    return resp\nend\n\nfunction app.init()\n    -- Initialize zipkin client that will be send spans every 5 seconds\n    local tracer = zipkin.new({\n        base_url = 'localhost:9411/api/v2/spans',\n        api_method = 'POST',\n        report_interval = 5,\n        on_error = function(err) log.error(err) end,\n    }, Sampler)\n    opentracing.set_global_tracer(tracer)\n\n    local httpd = http_server.new(HOST, PORT)\n    httpd:route({ path = '/format', method = 'GET' }, handler)\n    httpd:start()\nend\n\napp.init()\n\nreturn app\n```\n\nPublisher HTTP server\n```lua\n#!/usr/bin/env tarantool\n\nlocal http_server = require('http.server')\nlocal fiber = require('fiber')\nlocal log = require('log')\nlocal zipkin = require('zipkin.tracer')\nlocal opentracing = require('opentracing')\n\nlocal app = {}\n\nlocal Sampler = {\n    sample = function() return true end,\n}\n\nlocal HOST = '0.0.0.0'\nlocal PORT = '33303'\n\nlocal function handler(req)\n    local ctx, err = opentracing.http_extract(req.headers)\n\n    if ctx == nil then\n        local resp = req:render({ text = err })\n        resp.status = 400\n        return resp\n    end\n\n    local hello = req:query_param('hello')\n    local span = opentracing.start_span_from_context(ctx, 'print_string')\n    span:set_component('publisher')\n    span:set_server_kind()\n    span:set_http_method(req.method)\n    span:set_http_path(req.path)\n\n    -- Simulate long request processing\n    fiber.sleep(3)\n\n    io.write(hello, '\\n')\n    local resp = req:render({text = '' })\n    resp.status = 200\n    span:set_http_status_code(resp.status)\n    span:finish()\n    return resp\nend\n\nfunction app.init()\n    local tracer = zipkin.new({\n        base_url = 'localhost:9411/api/v2/spans',\n        api_method = 'POST',\n        report_interval = 5,\n        on_error = function(err) log.error(err) end,\n    }, Sampler)\n    opentracing.set_global_tracer(tracer)\n\n    local httpd = http_server.new(HOST, PORT)\n    httpd:route({ path = '/print', method = 'GET' }, handler)\n    httpd:start()\nend\n\napp.init()\n\nreturn app\n\n```\n\nClient\n```lua\n#!/usr/bin/env tarantool\n\nlocal http_client = require('http.client')\nlocal json = require('json')\nlocal log = require('log')\nlocal fiber = require('fiber')\nlocal zipkin = require('zipkin.tracer')\nlocal opentracing = require('opentracing')\n\nlocal app = {}\n\n-- Process all requests\nlocal Sampler = {\n    sample = function() return true end,\n}\n\nlocal function url_encode(str)\n    local res = string.gsub(str, '[^a-zA-Z0-9_]',\n        function(c)\n            return string.format('%%%02X', string.byte(c))\n        end\n    )\n    return res\nend\n\n-- Client part to formatter\nlocal formatter_url = 'http://localhost:33302/format'\nlocal function format_string(ctx, str)\n    local span = opentracing.start_span_from_context(ctx, 'format_string')\n    local httpc = http_client.new()\n    span:set_component('client')\n    span:set_client_kind()\n    span:set_http_method('GET')\n    span:set_http_url(formatter_url)\n\n    -- Use http headers as carrier\n    local headers = {\n        ['content-type'] = 'application/json'\n    }\n    opentracing.http_inject(span:context(), headers)\n\n    -- Simulate problems with network\n    fiber.sleep(1)\n    local resp = httpc:get(formatter_url .. '?helloto=' .. url_encode(str),\n            { headers = headers })\n    fiber.sleep(1)\n\n    span:set_http_status_code(resp.status)\n    if resp.status ~= 200 then\n        error('Format string error: ' .. json.encode(resp))\n    end\n    local result = resp.body\n    -- Log result\n    span:log_kv({\n        event = 'String format',\n        value = result\n    })\n    span:finish()\n    return result\nend\n\n-- Client part to publisher\nlocal printer_url = 'http://localhost:33303/print'\nlocal function print_string(ctx, str)\n    local span = opentracing.start_span_from_context(ctx, 'print_string')\n    local httpc = http_client.new()\n    span:set_component('client')\n    span:set_client_kind()\n    span:set_http_method('GET')\n    span:set_http_url(printer_url)\n\n    local headers = {\n        ['content-type'] = 'application/json'\n    }\n    opentracing.http_inject(span:context(), headers)\n\n    -- Simulate problems with network\n    fiber.sleep(1)\n    local resp = httpc:get(printer_url .. '?hello=' .. url_encode(str),\n            { headers = headers })\n    fiber.sleep(1)\n\n    span:set_http_status_code(resp.status)\n    if resp.status ~= 200 then\n        error('Print string error: ' .. json.encode(resp))\n    end\n    span:finish()\nend\n\nfunction app.init()\n    -- Initialize Zipkin tracer\n    local tracer = zipkin.new({\n        base_url = 'localhost:9411/api/v2/spans',\n        api_method = 'POST',\n        report_interval = 0,\n        on_error = function(err) log.error(err) end,\n    }, Sampler)\n    opentracing.set_global_tracer(tracer)\n\n    -- Initialize root span\n    local span = opentracing.start_span('Hello-world')\n\n    local hello_to = 'world'\n    local greeting = 'my greeting'\n    span:set_component('client')\n    -- Set service type\n    span:set_client_kind()\n    -- Set tag with metadata\n    span:set_tag('hello-to', hello_to)\n    -- Add data to baggage\n    span:set_baggage_item('greeting', greeting)\n\n    local ctx = span:context()\n    local formatted_string = format_string(ctx, hello_to)\n    print_string(ctx, formatted_string)\n    span:finish()\nend\n\napp.init()\n\nos.exit(0)\n\n```\n\n* Check results on [http://localhost:9411/zipkin](http://localhost:9411/zipkin)\n\n\n### Tarantool Cartridge\n\n![cartridge-example-img](https://user-images.githubusercontent.com/8830475/68297520-2e543e00-00a8-11ea-9517-f9567dc3c808.png)\n\n*Complete source code see [here](/examples/cartridge)*\n\nOpentracing could be used with [Tarantool Cartridge](https://github.com/tarantool/cartridge).\n\nThis example is pretty similar to previous. We will have several roles\nthat communicate via rpc_call.\n\n#### Basics\n\nBefore describing let's define some restrictions of \"tracing in Tarantool\".\nRemote communications between tarantools are made using `net.box` module.\nIt allows to send only primitive types (except functions) and doesn't have\ncontainers for request context (as headers in HTTP).\nThen you should transfer span context explicitly as raw table as additional argument\nin your function.\n\n```lua\n-- Create span\nlocal span = opentracing.start_span('span')\n\n-- Create context carrier\nlocal rpc_context = {}\nopentracing.map_inject(span:context(), rpc_context)\n\n-- Pass context explicitly as additional argument\nlocal res, err = cartridge.rpc_call('role', 'fun', {rpc_context, ...})\n```\n\n#### Using inside roles\n\nThe logic of tracing fits into a separate permanent role.\nLet's define it:\n\n```lua\nlocal opentracing = require('opentracing')\nlocal zipkin = require('zipkin.tracer')\n\nlocal log = require('log')\n\n-- config = {\n--     base_url = 'localhost:9411/api/v2/spans',\n--     api_method = 'POST',\n--     report_interval = 5,    -- in seconds\n--     spans_limit = 1e4,      -- amount of spans that could be stored locally\n-- }\n\nlocal function apply_config(config)\n    -- sample all requests\n    local sampler = { sample = function() return true end }\n\n    local tracer = zipkin.new({\n        base_url = config.base_url,\n        api_method = config.api_method,\n        report_interval = config.report_interval,\n        spans_limit = config.spans_limit,\n        on_error = function(err) log.error('zipkin error: %s', err) end,\n    }, sampler)\n\n    -- Setup global tracer for easy access from another modules\n    opentracing.set_global_tracer(tracer)\n\n    return true\nend\n\nreturn {\n    role_name = 'tracing',\n    apply_config = apply_config,\n    dependencies = {},\n    -- Role will be hidden from WebUI\n    -- but constantly enabled on all instances,\n    -- no need to specify it as dependency for other roles\n    permanent = true,\n}\n```\n\nThen you can use this role as dependency:\n```lua\nlocal opentracing = require('opentracing')\nlocal membership = require('membership')\n\nlocal role_name = 'formatter'\nlocal template = 'Hello, %s'\n\nlocal service_uri = ('%s@%s'):format(role_name, membership.myself().uri)\n\nlocal function format(ctx, input)\n    -- Extract tracing context from request context\n    local context = opentracing.map_extract(ctx)\n    local span = opentracing.start_span_from_context(context, 'format')\n    span:set_component(service_uri)\n\n    local result, err\n    if input == '' then\n        err = 'Empty string'\n        span:set_error(err)\n    else\n        result = template:format(input)\n    end\n\n    span:finish()\n\n    return result, err\nend\n\nlocal function init(_)\n    return true\nend\n\nlocal function stop()\nend\n\nlocal function validate_config(_, _)\n    return true\nend\n\nlocal function apply_config(_, _)\n    return true\nend\n\nreturn {\n    format = format,\n\n    role_name = role_name,\n    init = init,\n    stop = stop,\n    validate_config = validate_config,\n    apply_config = apply_config,\n    dependencies = {},\n}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarantool%2Ftracing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftarantool%2Ftracing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarantool%2Ftracing/lists"}