ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
otel.c File Reference

Go to the source code of this file.

Macros

#define ASCIICHAT_OTEL_IMPLEMENTATION
#define OTEL_NAME_BUFFER_SIZE   256
#define OTEL_VERSION_BUFFER_SIZE   32
#define OTEL_BODY_BUFFER_SIZE   4096
#define OTEL_URL_BUFFER_SIZE   2048
#define OTEL_NAME_EXTENDED_BUFFER_SIZE   1200
#define OTEL_WAIT_SNAPSHOT_CAPACITY   256
#define ASCII_OTEL_OPERATION(id, name, realtime)
#define ASCII_OTEL_METRIC(id, name, type, unit)
#define ASCII_STATS_COUNTER(id, metric_id, name, unit)
#define ASCII_STATS_GAUGE(id, metric_id, name, unit)
#define ASCII_STATS_DURATION(id, metric_id, name, unit)
#define ASCII_STATS_COUNTER(id, metric_id, name, unit)
#define ASCII_STATS_GAUGE(id, metric_id, name, unit)
#define ASCII_STATS_DURATION(id, metric_id, name, unit)

Enumerations

enum  { stats_metric_count }

Functions

const char * ascii_otel_current_operation (void)
bool ascii_otel_enabled (void)
void ascii_otel_metric (ascii_otel_metric_t metric, double value)
ascii_otel_metric_t ascii_otel_stats_metric (stats_record_kind_t kind, unsigned id)
void ascii_otel_stats_attach (stats_scope_t *scope)
void ascii_otel_pipeline_progress (unsigned stage, unsigned expected)
double ascii_otel_pipeline_age (uint64_t now, uint64_t capture, uint64_t progress, bool expected)
void ascii_otel_scope_begin (ascii_otel_scope_t *s, ascii_otel_operation_t op)
void ascii_otel_scope_parent (ascii_otel_scope_t *s, ascii_otel_operation_t op, const gotel_context_t *parent)
void ascii_otel_scope_end (ascii_otel_scope_t *s)
void ascii_otel_ui_usage (const char *screen, const char *event, uint64_t duration_ns)
void ascii_otel_call_session_used (const char *session_string)
void ascii_otel_packet (ascii_otel_scope_t *scope, uint16_t type, size_t payload_size, const char *action, int transport)
void ascii_otel_shutdown (void)
asciichat_error_t ascii_otel_init (const char *mode, int argc, char **argv)
void ascii_otel_log (int level, const char *file, int line, const char *function, const char *format, va_list args)
gotel_context_t ascii_otel_context (void)
void ascii_otel_scope_int (ascii_otel_scope_t *s, const char *key, int64_t value)
void ascii_otel_scope_string (ascii_otel_scope_t *s, const char *key, const char *value)
void ascii_otel_exit (int code)
void ascii_otel_frame_rejected (const char *validator, const char *reason, size_t bytes, uint32_t width, uint32_t height, asciichat_error_t code)
ascii_otel_operation_t ascii_otel_failure_operation (asciichat_error_t code, const char *origin)
void ascii_otel_error_code (asciichat_error_t code, const char *message)
void ascii_otel_error (const char *message)
void ascii_otel_http (ascii_otel_scope_t *s, const char *hostname, const char *path)
void ascii_otel_http_status (ascii_otel_scope_t *s, int status)
int ascii_otel_sqlite_prepare (sqlite3 *db, const char *sql, sqlite3_stmt **statement)
void ascii_otel_sqlite (sqlite3 *db, const gotel_query_t *queries, size_t count)
void ascii_otel_sqlite_catalog (sqlite3 *db, const gotel_query_catalog_t *catalog)

Detailed Description

Native telemetry lifecycle and ascii-chat instrumentation.

Definition in file otel.c.

Macro Definition Documentation

◆ ASCII_OTEL_METRIC

#define ASCII_OTEL_METRIC ( id,
name,
type,
unit )
Value:
{name, unit, OTLP_METRIC_##type, metric_bounds, sizeof(metric_bounds) / sizeof(metric_bounds[0])},

◆ ASCII_OTEL_OPERATION

#define ASCII_OTEL_OPERATION ( id,
name,
realtime )
Value:
{name, realtime},

◆ ASCII_STATS_COUNTER [1/2]

#define ASCII_STATS_COUNTER ( id,
metric_id,
name,
unit )
Value:
case (STATS_RECORD_COUNTER << 16) | STATS_COUNTER_##id: \
metric = ASCII_OTEL_METRIC_##metric_id; \
break;

◆ ASCII_STATS_COUNTER [2/2]

#define ASCII_STATS_COUNTER ( id,
metric_id,
name,
unit )
Value:
+1

◆ ASCII_STATS_DURATION [1/2]

#define ASCII_STATS_DURATION ( id,
metric_id,
name,
unit )
Value:
case (STATS_RECORD_DURATION << 16) | STATS_DURATION_##id: \
metric = ASCII_OTEL_METRIC_##metric_id; \
break;

◆ ASCII_STATS_DURATION [2/2]

#define ASCII_STATS_DURATION ( id,
metric_id,
name,
unit )
Value:
+1

◆ ASCII_STATS_GAUGE [1/2]

#define ASCII_STATS_GAUGE ( id,
metric_id,
name,
unit )
Value:
case (STATS_RECORD_GAUGE << 16) | STATS_GAUGE_##id: \
metric = ASCII_OTEL_METRIC_##metric_id; \
break;

◆ ASCII_STATS_GAUGE [2/2]

#define ASCII_STATS_GAUGE ( id,
metric_id,
name,
unit )
Value:
+1

◆ ASCIICHAT_OTEL_IMPLEMENTATION

#define ASCIICHAT_OTEL_IMPLEMENTATION

Definition at line 2 of file otel.c.

◆ OTEL_BODY_BUFFER_SIZE

#define OTEL_BODY_BUFFER_SIZE   4096

Definition at line 7 of file otel.c.

Referenced by ascii_otel_error(), ascii_otel_error_code(), and ascii_otel_log().

◆ OTEL_NAME_BUFFER_SIZE

#define OTEL_NAME_BUFFER_SIZE   256

Definition at line 5 of file otel.c.

Referenced by ascii_otel_packet().

◆ OTEL_NAME_EXTENDED_BUFFER_SIZE

#define OTEL_NAME_EXTENDED_BUFFER_SIZE   1200

Definition at line 9 of file otel.c.

Referenced by ascii_otel_http().

◆ OTEL_URL_BUFFER_SIZE

#define OTEL_URL_BUFFER_SIZE   2048

Definition at line 8 of file otel.c.

Referenced by ascii_otel_http().

◆ OTEL_VERSION_BUFFER_SIZE

#define OTEL_VERSION_BUFFER_SIZE   32

Definition at line 6 of file otel.c.

Referenced by ascii_otel_packet().

◆ OTEL_WAIT_SNAPSHOT_CAPACITY

#define OTEL_WAIT_SNAPSHOT_CAPACITY   256

Definition at line 12 of file otel.c.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
Enumerator
stats_metric_count 

Definition at line 168 of file otel.c.

168 {
170#define ASCII_STATS_COUNTER(id, metric_id, name, unit) +1
171#define ASCII_STATS_GAUGE(id, metric_id, name, unit) +1
172#define ASCII_STATS_DURATION(id, metric_id, name, unit) +1
173#include <ascii-chat/stats/telemetry.def>
174#undef ASCII_STATS_COUNTER
175#undef ASCII_STATS_GAUGE
176#undef ASCII_STATS_DURATION
177};
@ stats_metric_count
Definition otel.c:169

Function Documentation

◆ ascii_otel_call_session_used()

void ascii_otel_call_session_used ( const char * session_string)

Definition at line 443 of file otel.c.

443 {
444 if (!session_string || !session_string[0])
445 return;
446 // Call usage must not inherit a realtime packet's sampling decision.
447 gotel_context_t root = {0};
448 ASCII_OTEL_SCOPE_PARENT(CALL_SESSION_USED, &root);
449 ASCII_OTEL_ATTR_STRING("ascii-chat.session.string", session_string);
450}
#define ASCII_OTEL_SCOPE_PARENT(id, parent)
Definition otel.h:69
#define ASCII_OTEL_ATTR_STRING(key, value)
Definition otel.h:66

References ASCII_OTEL_ATTR_STRING, and ASCII_OTEL_SCOPE_PARENT.

◆ ascii_otel_context()

gotel_context_t ascii_otel_context ( void )

Definition at line 725 of file otel.c.

725 {
726 return gotel_current_context();
727}

Referenced by ascii_otel_error_code(), ascii_otel_frame_rejected(), and mcp_dispatch().

◆ ascii_otel_current_operation()

const char * ascii_otel_current_operation ( void )

Definition at line 153 of file otel.c.

153 {
154 return current_scope && current_scope->scope.operation < ASCII_OTEL_OPERATION_COUNT
155 ? operations[current_scope->scope.operation].name
156 : "";
157}
@ ASCII_OTEL_OPERATION_COUNT
Definition otel.h:10

References ASCII_OTEL_OPERATION_COUNT.

Referenced by ascii_otel_error_code().

◆ ascii_otel_enabled()

bool ascii_otel_enabled ( void )

Definition at line 158 of file otel.c.

158 {
159 return atomic_ptr_load(&host) != NULL;
160}

Referenced by ascii_otel_error_code(), and ascii_otel_pipeline_progress().

◆ ascii_otel_error()

void ascii_otel_error ( const char * message)

Definition at line 804 of file otel.c.

804 {
805 char redacted[OTEL_BODY_BUFFER_SIZE];
806 int written = safe_snprintf(redacted, sizeof(redacted), "%s", message ? message : "");
807 if (written < 0)
808 return;
809 url_redact_diagnostic(redacted, (size_t)written >= sizeof(redacted));
810 gotel_current_error("ascii_chat.error", redacted);
811}
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Platform-safe snprintf wrapper.
void url_redact_diagnostic(char *text, bool truncated)
Definition url.c:31
#define OTEL_BODY_BUFFER_SIZE
Definition otel.c:7

References OTEL_BODY_BUFFER_SIZE, safe_snprintf(), and url_redact_diagnostic().

Referenced by mcp_error().

◆ ascii_otel_error_code()

void ascii_otel_error_code ( asciichat_error_t code,
const char * message )

Definition at line 776 of file otel.c.

776 {
777 if (code == ASCIICHAT_OK || code == ERROR_SIGNAL_INTERRUPT || code == ERROR_NETWORK_INCOMPLETE)
778 return;
779 char redacted[OTEL_BODY_BUFFER_SIZE];
780 int written = safe_snprintf(redacted, sizeof(redacted), "%s", message ? message : "");
781 if (written < 0)
782 return;
783 url_redact_diagnostic(redacted, (size_t)written >= sizeof(redacted));
784 gotel_current_error("ascii_chat.error", redacted);
785 if (!ascii_otel_enabled())
786 return;
787 const char *origin = ascii_otel_current_operation();
789 if (operation == ASCII_OTEL_OPERATION_COUNT)
790 return;
791 const char *category = operation == ASCII_OTEL_FAILURE_CRYPTO ? "crypto"
792 : operation == ASCII_OTEL_FAILURE_PROTOCOL ? "protocol"
793 : "discovery";
794 gotel_context_t parent = ascii_otel_context();
796 if (!scope_retained(&report, operation, &parent))
797 return;
798 gotel_attr_string(&report.scope, "ascii_chat.failure.category", category);
799 gotel_attr_string(&report.scope, "ascii_chat.failure.operation", origin);
800 gotel_attr_int(&report.scope, "asciichat_errno.code", code);
801 gotel_attr_string(&report.scope, "asciichat_errno.code_message", asciichat_error_string(code));
802 gotel_error(&report.scope, category, redacted);
803}
@ ERROR_NETWORK_INCOMPLETE
Definition error_codes.h:90
@ ERROR_SIGNAL_INTERRUPT
@ ASCIICHAT_OK
Definition error_codes.h:46
int32_t vec_t __attribute__((vector_size(4 *4)))
SESSION_CREATED (PACKET_TYPE_ACIP_SESSION_CREATED) - Session created response.
Definition vector.h:42
bool ascii_otel_enabled(void)
Definition otel.c:158
gotel_context_t ascii_otel_context(void)
Definition otel.c:725
ascii_otel_operation_t ascii_otel_failure_operation(asciichat_error_t code, const char *origin)
Definition otel.c:763
const char * ascii_otel_current_operation(void)
Definition otel.c:153
void ascii_otel_scope_end(ascii_otel_scope_t *s)
Definition otel.c:425
ascii_otel_operation_t
Definition otel.h:6
struct ascii_otel_scope ascii_otel_scope_t
ascii otel scope is intentionally a small, concrete record. Fields such as scope, retained,...

References ascii_otel_context(), ascii_otel_current_operation(), ascii_otel_enabled(), ascii_otel_failure_operation(), ASCII_OTEL_OPERATION_COUNT, ascii_otel_scope_end(), ASCIICHAT_OK, ERROR_NETWORK_INCOMPLETE, ERROR_SIGNAL_INTERRUPT, OTEL_BODY_BUFFER_SIZE, safe_snprintf(), and url_redact_diagnostic().

◆ ascii_otel_exit()

void ascii_otel_exit ( int code)

Definition at line 736 of file otel.c.

736 {
737 gotel_attr_int(&command_scope, "process.exit.code", code);
738 if (code)
739 gotel_error(&command_scope, "ascii_chat.exit", "Command failed");
740 else {
741 command_scope.failed = false;
742 if (command_scope.span)
743 (void)otel_status(otlp_span_set_status(command_scope.span, OTLP_STATUS_CODE_UNSET, ""), "otlp_span_set_status");
744 }
745}

Referenced by main().

◆ ascii_otel_failure_operation()

ascii_otel_operation_t ascii_otel_failure_operation ( asciichat_error_t code,
const char * origin )

Definition at line 763 of file otel.c.

763 {
764 if (code == ASCIICHAT_OK || code == ERROR_SIGNAL_INTERRUPT || code == ERROR_NETWORK_INCOMPLETE)
766 if ((code >= ERROR_CRYPTO && code <= ERROR_CRYPTO_VERIFICATION) || code == ERROR_CRYPTO_INIT ||
768 return ASCII_OTEL_FAILURE_CRYPTO;
769 if (code == ERROR_NETWORK_PROTOCOL || code == ERROR_NETWORK_SIZE || code == ERROR_UNKNOWN_PACKET)
770 return ASCII_OTEL_FAILURE_PROTOCOL;
771 if ((code >= ERROR_SESSION_NOT_FOUND && code <= ERROR_SESSION_FULL) || code == ERROR_ACDS_STRING_TAKEN ||
772 code == ERROR_ACDS_STRING_INVALID || (origin && !strncmp(origin, "ascii-chat.discovery.", 21)))
773 return ASCII_OTEL_FAILURE_DISCOVERY;
775}
@ ERROR_UNKNOWN_PACKET
Definition error_codes.h:88
@ ERROR_INVALID_PASSWORD
Definition error_codes.h:83
@ ERROR_ACDS_STRING_INVALID
Definition error_codes.h:86
@ ERROR_ACDS_STRING_TAKEN
Definition error_codes.h:85
@ ERROR_CRYPTO_VERIFICATION
Definition error_codes.h:97
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
@ ERROR_INVALID_SIGNATURE
Definition error_codes.h:84
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77
@ ERROR_SESSION_FULL
Definition error_codes.h:82
@ ERROR_CRYPTO_INIT
Definition error_codes.h:53
@ ERROR_CRYPTO
Definition error_codes.h:93
@ ERROR_SESSION_NOT_FOUND
Definition error_codes.h:81

References ASCII_OTEL_OPERATION_COUNT, ASCIICHAT_OK, ERROR_ACDS_STRING_INVALID, ERROR_ACDS_STRING_TAKEN, ERROR_CRYPTO, ERROR_CRYPTO_INIT, ERROR_CRYPTO_VERIFICATION, ERROR_INVALID_PASSWORD, ERROR_INVALID_SIGNATURE, ERROR_NETWORK_INCOMPLETE, ERROR_NETWORK_PROTOCOL, ERROR_NETWORK_SIZE, ERROR_SESSION_FULL, ERROR_SESSION_NOT_FOUND, ERROR_SIGNAL_INTERRUPT, and ERROR_UNKNOWN_PACKET.

Referenced by ascii_otel_error_code().

◆ ascii_otel_frame_rejected()

void ascii_otel_frame_rejected ( const char * validator,
const char * reason,
size_t bytes,
uint32_t width,
uint32_t height,
asciichat_error_t code )

Definition at line 746 of file otel.c.

747 {
748 ascii_otel_metric(ASCII_OTEL_METRIC_FRAME_VALIDATION_REJECTED, 1);
749 gotel_context_t parent = ascii_otel_context();
750 ascii_otel_scope_t otel_scope __attribute__((cleanup(ascii_otel_scope_end))) = {0};
751 if (!scope_retained(&otel_scope, ASCII_OTEL_FRAME_VALIDATION_REJECTED, &parent))
752 return;
753 ASCII_OTEL_ATTR_STRING("frame.validation.validator", validator);
754 ASCII_OTEL_ATTR_STRING("frame.validation.reason", reason);
755 ASCII_OTEL_ATTR_INT("frame.size_bytes", (int64_t)bytes);
756 ASCII_OTEL_ATTR_INT("frame.width", width);
757 ASCII_OTEL_ATTR_INT("frame.height", height);
758 ASCII_OTEL_ATTR_INT("asciichat_errno.code", code);
759 ASCII_OTEL_ATTR_STRING("asciichat_errno.code_message", asciichat_error_string(code));
760 if (otel_scope.retained)
761 gotel_error(&otel_scope.scope, "ascii_chat.invalid_frame", reason);
762}
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23
void ascii_otel_metric(ascii_otel_metric_t metric, double value)
Definition otel.c:161
#define ASCII_OTEL_ATTR_INT(key, value)
Definition otel.h:67

References ASCII_OTEL_ATTR_INT, ASCII_OTEL_ATTR_STRING, ascii_otel_context(), ascii_otel_metric(), ascii_otel_scope_end(), height, and width.

Referenced by session_frame_validate_contract().

◆ ascii_otel_http()

void ascii_otel_http ( ascii_otel_scope_t * s,
const char * hostname,
const char * path )

Definition at line 812 of file otel.c.

812 {
813 if (!s->retained || !hostname || !path)
814 return;
815 gotel_error(&s->scope, "http.transport", "HTTP operation did not complete");
817 int n = safe_snprintf(url, sizeof(url), "https://%s%s", hostname, path);
818 if (n >= 0 && (size_t)n < sizeof(url) &&
819 otel_status(gotel_http_name(name, sizeof(name), "ascii-chat", false, "GET", url), "Name HTTP telemetry span") ==
820 ASCIICHAT_OK) {
821 if (s->scope.span)
822 (void)otel_status(otlp_span_set_name(s->scope.span, name), "otlp_span_set_name");
823 gotel_http_attributes(&s->scope, "GET", 0);
824 gotel_attr_string(&s->scope, "server.address", hostname);
825 gotel_attr_string(&s->scope, "url.scheme", "https");
826 }
827}
#define OTEL_NAME_EXTENDED_BUFFER_SIZE
Definition otel.c:9
#define OTEL_URL_BUFFER_SIZE
Definition otel.c:8
gotel_scope_t scope
Definition otel.h:24
bool retained
Definition otel.h:25

References ASCIICHAT_OK, OTEL_NAME_EXTENDED_BUFFER_SIZE, OTEL_URL_BUFFER_SIZE, ascii_otel_scope::retained, safe_snprintf(), and ascii_otel_scope::scope.

Referenced by https_get().

◆ ascii_otel_http_status()

void ascii_otel_http_status ( ascii_otel_scope_t * s,
int status )

Definition at line 828 of file otel.c.

828 {
829 if (s->retained) {
830 if (status > 0 && status < 400) {
831 s->scope.failed = false;
832 if (s->scope.span) {
833 (void)otel_status(otlp_span_set_status(s->scope.span, OTLP_STATUS_CODE_UNSET, ""), "otlp_span_set_status");
834 gotel_attr_string(&s->scope, "error.type", "");
835 }
836 }
837 gotel_http_attributes(&s->scope, "GET", status);
838 }
839}

References ascii_otel_scope::retained, and ascii_otel_scope::scope.

Referenced by https_get().

◆ ascii_otel_init()

asciichat_error_t ascii_otel_init ( const char * mode,
int argc,
char ** argv )

Definition at line 555 of file otel.c.

555 {
556 // Test mode is a hard export prohibition, including explicit endpoints and CLI overrides.
557 if (is_test_environment())
558 return ASCIICHAT_OK;
559 const options_t *o = options_get();
560 const char *endpoint = o->otel_endpoint;
561 if (!endpoint || endpoint[0] == '\0')
562 return ASCIICHAT_OK;
563 bool endpoint_cli = false, timeout_cli = false;
564 for (int i = 1; i < argc; ++i) {
565 endpoint_cli |= !strcmp(argv[i], "--otel-endpoint") || !strncmp(argv[i], "--otel-endpoint=", 16);
566 timeout_cli |= !strcmp(argv[i], "--otel-timeout") || !strncmp(argv[i], "--otel-timeout=", 15);
567 }
568 unsigned signals;
569 asciichat_error_t parsed = otel_status(gotel_parse_signals(o->otel_enabled_telemetry, &signals),
570 "Parse otel-enabled-telemetry (traces,logs,metrics or none)");
571 if (parsed != ASCIICHAT_OK)
572 return parsed;
574 o->otel_queue_capacity < 2 || (o->otel_queue_capacity & (o->otel_queue_capacity - 1)) || o->otel_timeout < 1 ||
576 return SET_ERRNO(ERROR_CONFIG, "Invalid telemetry sampling, queue capacity, or interval");
577 const char *log_names[] = {"dev", "debug", "info", "warn", "error", "fatal"};
578 const int log_levels[] = {LOG_DEV, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL};
579 minimum_log_level = -1;
580 for (size_t i = 0; i < sizeof(log_levels) / sizeof(log_levels[0]); ++i)
581 if (strcmp(o->otel_log_level, log_names[i]) == 0)
582 minimum_log_level = log_levels[i];
583 if (minimum_log_level < 0)
584 return SET_ERRNO(ERROR_CONFIG, "Invalid otel-log-level");
585 if (!signals)
586 return ASCIICHAT_OK;
587 gotel_options_t config = {0};
588 config.application_name = "ascii-chat";
589 config.disable_span_name_prefix = true;
590 char service_name[OPTIONS_BUFF_SIZE];
591 int n = safe_snprintf(service_name, sizeof(service_name), "ascii-chat-%s", mode);
592 if (n < 0 || (size_t)n >= sizeof(service_name))
593 return SET_ERRNO(ERROR_CONFIG, "Telemetry service name too long");
594 config.exporter.service_name = service_name;
595 // The exporter observes its batch interval during final flush as well.
596 // Keep it short enough to deliver all signals within the shutdown budget.
597 config.exporter.batch_ms = 25;
598 config.exporter.queue_capacity = (size_t)o->otel_queue_capacity;
599 config.exporter.connect_timeout_ms = (uint32_t)o->otel_timeout;
600 config.exporter.read_timeout_ms = (uint32_t)o->otel_timeout;
601 config.exporter.flush_timeout_ms = (uint32_t)o->otel_timeout;
602 otlp_resource_attr_t resources[] = {
603 {.key = "ascii_chat.mode", .value = {.type = OTLP_VALUE_STRING, .v = {.string_val = mode}}},
604 {.key = "service.version", .value = {.type = OTLP_VALUE_STRING, .v = {.string_val = ASCII_CHAT_VERSION_FULL}}},
605 };
606 config.exporter.resource_attributes = resources;
607 config.exporter.n_resource_attributes = sizeof(resources) / sizeof(resources[0]);
608 config.signals = signals;
609 config.realtime_sample_rate = o->otel_realtime_sample_rate;
610 config.realtime_max_per_second = (uint32_t)o->otel_realtime_max_per_second;
611 config.metrics_interval_ms = (uint32_t)o->otel_metrics_interval;
612 config.monotonic_ns = time_get_ns;
613 config.realtime_ns = telemetry_realtime_ns;
614 config.operations = operations;
615 config.operation_count = ASCII_OTEL_OPERATION_COUNT;
616 config.exporter_event = exporter_event;
617 config.metrics = metrics;
618 config.metric_count = ASCII_OTEL_METRIC_COUNT;
619#ifndef NDEBUG
620 config.watchdog.snapshot = watchdog_snapshot;
621 config.watchdog.decorate = watchdog_decorate;
622 config.watchdog.operation = ASCII_OTEL_THREAD_DEADLOCK;
623 config.watchdog.metric = ASCII_OTEL_METRIC_DEADLOCK_THREADS;
624#endif
625 gotel_command_args_t command_args = {0};
626 // Addresses are positional in native client/server modes, with optional ports.
627 char address_port[OPTIONS_BUFF_SIZE + 32], address6_port[OPTIONS_BUFF_SIZE + 32];
628 char address_brackets[OPTIONS_BUFF_SIZE + 32], address6_brackets[OPTIONS_BUFF_SIZE + 32];
629 char address_brackets_port[OPTIONS_BUFF_SIZE + 32], address6_brackets_port[OPTIONS_BUFF_SIZE + 32];
630 SAFE_SNPRINTF(address_port, sizeof(address_port), "%s:%d", o->address, o->port);
631 SAFE_SNPRINTF(address6_port, sizeof(address6_port), "%s:%d", o->address6, o->port);
632 SAFE_SNPRINTF(address_brackets, sizeof(address_brackets), "[%s]", o->address);
633 SAFE_SNPRINTF(address6_brackets, sizeof(address6_brackets), "[%s]", o->address6);
634 SAFE_SNPRINTF(address_brackets_port, sizeof(address_brackets_port), "[%s]:%d", o->address, o->port);
635 SAFE_SNPRINTF(address6_brackets_port, sizeof(address6_brackets_port), "[%s]:%d", o->address6, o->port);
636 const char *sensitive_values[] = {o->address, o->address6, address_port, address6_port,
637 address_brackets, address6_brackets, address_brackets_port, address6_brackets_port};
638 gotel_command_policy_t argument_policy = {
639#ifdef NDEBUG
640 .mode = GOTEL_COMMAND_REDACT_SELECTED,
641#else
642 .mode = GOTEL_COMMAND_REDACT_NONE,
643#endif
644 .rules = command_argument_rules,
645 .sensitive_values = sensitive_values,
646 .sensitive_value_count = sizeof(sensitive_values) / sizeof(sensitive_values[0]),
647 .rule_count = sizeof(command_argument_rules) / sizeof(command_argument_rules[0]),
648 };
649 asciichat_error_t arguments_status =
650 otel_status(gotel_command_args_init_policy(&command_args, "ascii-chat", mode, argc, (const char *const *)argv,
651 &argument_policy),
652 "Sanitize telemetry command arguments");
653 if (arguments_status != ASCIICHAT_OK)
654 return arguments_status;
655 config.argc = command_args.argc;
656 config.argv = command_args.argv;
657 gotel_env_storage_t env;
658 asciichat_error_t env_status =
659 otel_status(gotel_options_apply_env(&config, &env, OPT_OTEL_ENDPOINT_DEFAULT), "Configure OTEL environment");
660 if (env_status == ASCIICHAT_OK &&
661 (endpoint_cli || SAFE_GETENV("ASCII_CHAT_OTEL_ENDPOINT") || strcmp(endpoint, OPT_OTEL_ENDPOINT_DEFAULT)))
662 env_status = otel_status(gotel_options_set_endpoint(&config, &env, endpoint), "Configure telemetry endpoint");
663 if (timeout_cli || SAFE_GETENV("ASCII_CHAT_OTEL_TIMEOUT")) {
664 config.exporter.connect_timeout_ms = (uint32_t)o->otel_timeout;
665 config.exporter.read_timeout_ms = (uint32_t)o->otel_timeout;
666 config.exporter.flush_timeout_ms = (uint32_t)o->otel_timeout;
667 }
668 if (*o->otel_ca_file)
669 config.tls_ca_file = o->otel_ca_file;
670 if (env_status != ASCIICHAT_OK || !config.signals || !config.exporter.endpoint) {
671 gotel_command_args_destroy(&command_args);
672 return env_status;
673 }
674 gotel_host_t *h = NULL;
675 asciichat_error_t created = otel_status(gotel_create(&config, &h), "Initialize telemetry exporter");
676 if (created != ASCIICHAT_OK) {
677 gotel_command_args_destroy(&command_args);
678 return created;
679 }
680 gotel_set_logger(h, exporter_diagnostic, NULL);
681 shutdown_timeout_ns = (uint64_t)o->otel_timeout * NS_PER_MS_INT;
682 atomic_store(&stopping, false);
683 atomic_store(&exporter_queue_capacity, config.exporter.queue_capacity);
684 atomic_store(&worker_done, false);
685 if (platform_thread_create(&worker, "otel_export", export_worker, h) != 0) {
686 gotel_destroy(h);
687 gotel_command_args_destroy(&command_args);
688 return SET_ERRNO(ERROR_THREAD, "Cannot start telemetry exporter");
689 }
690 worker_started = true;
691 (void)atexit(ascii_otel_shutdown);
692 atomic_ptr_store(&host, h);
693 gotel_begin(h, &command_scope, ASCII_OTEL_CLI, NULL);
694 (void)otel_status(gotel_command_args_set_span(&command_scope, &command_args), "Attach telemetry command arguments");
695 gotel_command_args_destroy(&command_args);
696
697 log_info("Telemetry enabled: %s (%s)", config.exporter.endpoint, o->otel_enabled_telemetry);
698 return ASCIICHAT_OK;
699}
#define SET_ERRNO(code,...)
unsigned int uint32_t
Definition common.h:62
#define SAFE_GETENV(name)
Definition common.h:449
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:507
unsigned long long uint64_t
Definition common.h:63
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_CONFIG
Definition error_codes.h:52
@ ERROR_THREAD
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
#define NS_PER_MS_INT
Definition time.h:168
struct options_state options_t
Immutable, RCU-published snapshot of the application's runtime configuration.
#define OPT_OTEL_ENDPOINT_DEFAULT
Debug and Dev builds export to the local Spaniel Compose collector.
Definition options.h:354
const options_t * options_get(void)
Get pointer to current options struct (lock-free, thread-safe).
Definition rcu.c:511
#define OPTIONS_BUFF_SIZE
Buffer size for option string values.
Definition options.h:310
void ascii_otel_shutdown(void)
Definition otel.c:473
@ ASCII_OTEL_METRIC_COUNT
Definition otel.h:16
char otel_ca_file[256]
Definition options.h:1159
int otel_realtime_max_per_second
Definition options.h:1162
double otel_realtime_sample_rate
Definition options.h:1161
int otel_metrics_interval
Definition options.h:1163
int otel_queue_capacity
Definition options.h:1165
char otel_endpoint[256]
Definition options.h:1158
char otel_log_level[256]
Definition options.h:1157
char address6[256]
IPv6 bind address (server only).
Definition options.h:1008
int port
Server port number.
Definition options.h:1009
char otel_enabled_telemetry[256]
Definition options.h:1160
char address[256]
Server address (client) or bind address (server).
Definition options.h:1007
#define LOG_DEV
Definition types.h:38
#define LOG_DEBUG
Definition types.h:39
#define LOG_FATAL
Definition types.h:43
#define LOG_ERROR
Definition types.h:42
#define LOG_WARN
Definition types.h:41
#define LOG_INFO
Definition types.h:40

References options_state::address, options_state::address6, ASCII_OTEL_METRIC_COUNT, ASCII_OTEL_OPERATION_COUNT, ascii_otel_shutdown(), ASCIICHAT_OK, ERROR_CONFIG, ERROR_THREAD, LOG_DEBUG, LOG_DEV, LOG_ERROR, LOG_FATAL, LOG_INFO, log_info, LOG_WARN, NS_PER_MS_INT, OPT_OTEL_ENDPOINT_DEFAULT, OPTIONS_BUFF_SIZE, options_get(), options_state::otel_ca_file, options_state::otel_enabled_telemetry, options_state::otel_endpoint, options_state::otel_log_level, options_state::otel_metrics_interval, options_state::otel_queue_capacity, options_state::otel_realtime_max_per_second, options_state::otel_realtime_sample_rate, options_state::otel_timeout, options_state::port, SAFE_GETENV, SAFE_SNPRINTF, safe_snprintf(), and SET_ERRNO.

Referenced by main().

◆ ascii_otel_log()

void ascii_otel_log ( int level,
const char * file,
int line,
const char * function,
const char * format,
va_list args )

Definition at line 700 of file otel.c.

700 {
701 (void)file;
702 (void)line;
703 (void)function;
704 if (in_log || level < minimum_log_level || !atomic_ptr_load(&host))
705 return;
706 in_log = true;
707 gotel_host_t *h = retain();
708 if (h) {
709 char body[OTEL_BODY_BUFFER_SIZE];
710 int n = safe_vsnprintf(body, sizeof(body), format, args);
711 if (n >= 0) {
712 url_redact_diagnostic(body, (size_t)n >= sizeof(body));
713 otlp_severity_t severity = level >= LOG_FATAL ? OTLP_SEVERITY_FATAL
714 : level >= LOG_ERROR ? OTLP_SEVERITY_ERROR
715 : level >= LOG_WARN ? OTLP_SEVERITY_WARN
716 : level >= LOG_INFO ? OTLP_SEVERITY_INFO
717 : level >= LOG_DEBUG ? OTLP_SEVERITY_DEBUG
718 : OTLP_SEVERITY_TRACE;
719 gotel_log(h, severity, body, file, line, function);
720 }
721 release();
722 }
723 in_log = false;
724}
action_args_t args
Definition actions.c:78
int safe_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list ap)
Platform-safe vsnprintf wrapper.

References args, LOG_DEBUG, LOG_ERROR, LOG_FATAL, LOG_INFO, LOG_WARN, OTEL_BODY_BUFFER_SIZE, safe_vsnprintf(), and url_redact_diagnostic().

Referenced by log_msg().

◆ ascii_otel_metric()

◆ ascii_otel_packet()

void ascii_otel_packet ( ascii_otel_scope_t * scope,
uint16_t type,
size_t payload_size,
const char * action,
int transport )

Definition at line 451 of file otel.c.

452 {
453 if (!scope->retained || !scope->scope.span)
454 return;
455 const char *packet = acip_packet_type_name(type);
457 safe_snprintf(name, sizeof(name), "%s.%s", operations[scope->scope.operation].name, packet);
458 safe_snprintf(version, sizeof(version), "%d.%d.%d", PROTOCOL_VERSION_MAJOR, PROTOCOL_VERSION_MINOR,
460 (void)otel_status(otlp_span_set_name(scope->scope.span, name), "otlp_span_set_name");
461 gotel_attr_string(&scope->scope, "network.protocol.name", "acip");
462 gotel_attr_string(&scope->scope, "network.protocol.version", version);
463 gotel_attr_string(&scope->scope, "network.packet.name", packet);
464 gotel_attr_int(&scope->scope, "network.packet.type", type);
465 gotel_attr_int(&scope->scope, "network.packet.size", (int64_t)payload_size);
466 gotel_attr_string(&scope->scope, "network.packet.operation", action);
467 const char *carrier = transport == ACIP_TRANSPORT_TCP ? "tcp"
468 : transport == ACIP_TRANSPORT_WEBSOCKET ? "websocket"
469 : transport == ACIP_TRANSPORT_WEBRTC ? "webrtc"
470 : "unknown";
471 gotel_attr_string(&scope->scope, "acip.transport", carrier);
472}
deferred_action_t action
Definition actions.c:77
#define PROTOCOL_VERSION_MAJOR
Major protocol version number.
#define PROTOCOL_VERSION_MINOR
Minor protocol version number.
#define PROTOCOL_VERSION_PATCH
Patch protocol version number.
const char * acip_packet_type_name(uint16_t type)
Get human-readable name for packet type.
Definition handlers.c:648
@ ACIP_TRANSPORT_TCP
Raw TCP socket.
@ ACIP_TRANSPORT_WEBSOCKET
WebSocket over TCP.
@ ACIP_TRANSPORT_WEBRTC
WebRTC DataChannel (P2P).
#define OTEL_VERSION_BUFFER_SIZE
Definition otel.c:6
#define OTEL_NAME_BUFFER_SIZE
Definition otel.c:5

References acip_packet_type_name(), ACIP_TRANSPORT_TCP, ACIP_TRANSPORT_WEBRTC, ACIP_TRANSPORT_WEBSOCKET, action, OTEL_NAME_BUFFER_SIZE, OTEL_VERSION_BUFFER_SIZE, PROTOCOL_VERSION_MAJOR, PROTOCOL_VERSION_MINOR, PROTOCOL_VERSION_PATCH, ascii_otel_scope::retained, safe_snprintf(), and ascii_otel_scope::scope.

◆ ascii_otel_pipeline_age()

double ascii_otel_pipeline_age ( uint64_t now,
uint64_t capture,
uint64_t progress,
bool expected )

Definition at line 296 of file otel.c.

296 {
297 return expected && capture && progress && now >= capture && now >= progress && now - capture < 2 * NS_PER_SEC_INT
298 ? (double)(now - progress) / 1e9
299 : 0;
300}
#define NS_PER_SEC_INT
Definition time.h:169

References NS_PER_SEC_INT.

◆ ascii_otel_pipeline_progress()

void ascii_otel_pipeline_progress ( unsigned stage,
unsigned expected )

Definition at line 270 of file otel.c.

270 {
271 if (!ascii_otel_enabled())
272 return;
273 uint64_t now = time_get_ns();
274 if (stage == 0) {
275 if (!atomic_load(&pipeline_capture_ns)) {
276 atomic_store(&pipeline_display_ns, now);
277 atomic_store(&pipeline_encode_ns, now);
278 }
279 if (!(expected & 1))
280 atomic_store(&pipeline_display_ns, now);
281 if (!(expected & 2))
282 atomic_store(&pipeline_encode_ns, now);
283 atomic_store(&pipeline_expected, expected);
284 atomic_store(&pipeline_capture_ns, now);
285 } else if (stage == 1)
286 atomic_store(&pipeline_display_ns, now);
287 else if (stage == 2)
288 atomic_store(&pipeline_encode_ns, now);
289 else {
290 atomic_store(&pipeline_expected, 0);
291 atomic_store(&pipeline_capture_ns, 0);
292 ascii_otel_metric(ASCII_OTEL_METRIC_PIPELINE_DISPLAY_STALL, 0);
293 ascii_otel_metric(ASCII_OTEL_METRIC_PIPELINE_ENCODE_STALL, 0);
294 }
295}

References ascii_otel_enabled(), and ascii_otel_metric().

Referenced by session_pipeline_destroy(), and session_pipeline_run_main().

◆ ascii_otel_scope_begin()

void ascii_otel_scope_begin ( ascii_otel_scope_t * s,
ascii_otel_operation_t op )

Definition at line 387 of file otel.c.

387 {
388 ascii_otel_scope_parent(s, op, NULL);
389}
void ascii_otel_scope_parent(ascii_otel_scope_t *s, ascii_otel_operation_t op, const gotel_context_t *parent)
Definition otel.c:390

References ascii_otel_scope_parent().

◆ ascii_otel_scope_end()

void ascii_otel_scope_end ( ascii_otel_scope_t * s)

Definition at line 425 of file otel.c.

425 {
426 if (!s || !s->retained)
427 return;
428 gotel_end(&s->scope);
429 if (current_scope == s)
430 current_scope = s->previous;
431 s->retained = false;
432 release();
433}
struct ascii_otel_scope * previous
Definition otel.h:26

References ascii_otel_scope::previous, ascii_otel_scope::retained, and ascii_otel_scope::scope.

Referenced by ascii_otel_error_code(), and ascii_otel_frame_rejected().

◆ ascii_otel_scope_int()

void ascii_otel_scope_int ( ascii_otel_scope_t * s,
const char * key,
int64_t value )

Definition at line 728 of file otel.c.

728 {
729 if (s->retained)
730 gotel_attr_int(&s->scope, key, value);
731}

References ascii_otel_scope::retained, and ascii_otel_scope::scope.

Referenced by session_pipeline_run_main().

◆ ascii_otel_scope_parent()

void ascii_otel_scope_parent ( ascii_otel_scope_t * s,
ascii_otel_operation_t op,
const gotel_context_t * parent )

Definition at line 390 of file otel.c.

390 {
391 gotel_host_t *h = retain();
392 s->retained = h != NULL;
393 if (h) {
394 gotel_context_t context = gotel_current_context();
395 bool lifetime_parent = current_scope && (current_scope->scope.operation == ASCII_OTEL_SESSION_CLIENT_LIKE_RUN ||
396 current_scope->scope.operation == ASCII_OTEL_SESSION_ATTEMPT);
397 // Lifetime spans finish only at shutdown. Completed operations must remain
398 // inspectable while a server is running, including after abrupt termination.
399 if (!parent && context.valid) {
400 gotel_parent_mode_t mode =
401 lifetime_parent || memcmp(context.span_id, command_scope.context.span_id, sizeof(context.span_id)) == 0
402 ? GOTEL_PARENT_INDEPENDENT
403 : GOTEL_PARENT_BOUNDED;
404 context = gotel_operation_parent(context, mode);
405 parent = &context;
406 }
407 gotel_begin(h, &s->scope, (size_t)op, parent);
408 s->previous = current_scope;
409 current_scope = s;
410 }
411}

References ascii_otel_scope::previous, ascii_otel_scope::retained, and ascii_otel_scope::scope.

Referenced by ascii_otel_scope_begin().

◆ ascii_otel_scope_string()

void ascii_otel_scope_string ( ascii_otel_scope_t * s,
const char * key,
const char * value )

Definition at line 732 of file otel.c.

732 {
733 if (s->retained)
734 gotel_attr_string(&s->scope, key, value);
735}

References ascii_otel_scope::retained, and ascii_otel_scope::scope.

◆ ascii_otel_shutdown()

void ascii_otel_shutdown ( void )

Definition at line 473 of file otel.c.

473 {
474 gotel_host_t *h = atomic_ptr_exchange(&host, NULL);
475 if (!h)
476 return;
477 gotel_end(&command_scope);
478 uint64_t started = time_get_ns();
479 // Leave time for the rest of process teardown, even if the collector stalls.
480 uint64_t budget_ns = shutdown_timeout_ns < 400 * NS_PER_MS_INT ? shutdown_timeout_ns : 400 * NS_PER_MS_INT;
481 while (atomic_load_explicit(&readers, memory_order_seq_cst) && time_get_ns() - started < budget_ns)
483 bool producers_active = atomic_load_explicit(&readers, memory_order_seq_cst) != 0;
484 atomic_store_explicit(&stopping, true, memory_order_release);
485 if (worker_started) {
486 while (!atomic_load_explicit(&worker_done, memory_order_acquire) && time_get_ns() - started < budget_ns)
488 bool done = atomic_load_explicit(&worker_done, memory_order_acquire);
489 uint64_t elapsed_ns = time_get_ns() - started;
490 int joined = -1;
491 if (done && elapsed_ns < budget_ns) {
492#if !defined(__linux__) && !defined(_WIN32)
493 // The POSIX timed join implementation blocks on these platforms. The worker has
494 // finished using the exporter, so detach its remaining wrapper cleanup.
495 joined = thread_detach(&worker);
496#else
497 joined = thread_join_timeout(&worker, NULL, budget_ns - elapsed_ns);
498#endif
499 }
500 if (joined != 0) {
501 log_warn("Telemetry worker did not stop within its deadline; exporter retained until process exit");
502 return;
503 }
504 worker_started = false;
505 }
506 asciichat_error_t result = drain_result;
507 otlp_exporter_stats_t stats = {0};
508 (void)otel_status(gotel_stats(h, &stats), "gotel_stats");
509 // exit() can bypass scope cleanup in a producer. Do not free its host while
510 // it remains borrowed; the process will reclaim it after atexit handlers.
511 if (!producers_active)
512 gotel_destroy(h);
513 else
514 log_warn("Telemetry producer still active during process shutdown; exporter retained");
515 if (result != ASCIICHAT_OK)
516 log_warn("Telemetry flush deadline reached; pending records discarded");
517 log_debug("Telemetry exporter stopped: spans=%llu/%llu logs=%llu/%llu metrics=%llu/%llu network_errors=%llu "
518 "http_4xx=%llu dropped=%llu rejected=%llu",
519 (unsigned long long)stats.sent, (unsigned long long)stats.emitted, (unsigned long long)stats.sent_logs,
520 (unsigned long long)stats.emitted_logs, (unsigned long long)stats.sent_metrics,
521 (unsigned long long)stats.emitted_metrics, (unsigned long long)stats.network_err,
522 (unsigned long long)stats.http_4xx, (unsigned long long)(stats.dropped_full + stats.dropped_err),
523 (unsigned long long)stats.rejected_spans);
524}
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
void platform_sleep_ms(unsigned int ms)
Sleep for a specified number of milliseconds.

References ASCIICHAT_OK, log_debug, log_warn, NS_PER_MS_INT, and platform_sleep_ms().

Referenced by ascii_otel_init(), and main().

◆ ascii_otel_sqlite()

void ascii_otel_sqlite ( sqlite3 * db,
const gotel_query_t * queries,
size_t count )

Definition at line 844 of file otel.c.

844 {
845 gotel_host_t *h = retain();
846 if (!h)
847 return;
848 gotel_sqlite_t *a = NULL;
849 sequel_analysis_options_t analysis = {.format = true};
850 gotel_sqlite_options_t options = {.analysis = &analysis};
851 if (otel_status(gotel_sqlite_attach_with_options(h, db, "ascii-chat.storage", queries, count, &options, &a),
852 "Attach SQLite telemetry") != ASCIICHAT_OK)
853 log_warn("Could not attach SQLite telemetry");
854 release();
855}

References ASCIICHAT_OK, and log_warn.

◆ ascii_otel_sqlite_catalog()

void ascii_otel_sqlite_catalog ( sqlite3 * db,
const gotel_query_catalog_t * catalog )

The catalog accessor and its context must remain valid until the DB closes.

Definition at line 857 of file otel.c.

857 {
858 gotel_host_t *h = retain();
859 if (!h)
860 return;
861 gotel_sqlite_t *a = NULL;
862 sequel_analysis_options_t analysis = {.format = true};
863 gotel_sqlite_options_t options = {.analysis = &analysis};
864 if (otel_status(gotel_sqlite_attach_catalog_with_options(h, db, "ascii-chat.storage", catalog, &options, &a),
865 "Attach SQLite telemetry") != ASCIICHAT_OK)
866 log_warn("Could not attach SQLite telemetry");
867 release();
868}

References ASCIICHAT_OK, and log_warn.

Referenced by database_init().

◆ ascii_otel_sqlite_prepare()

◆ ascii_otel_stats_attach()

void ascii_otel_stats_attach ( stats_scope_t * scope)

Definition at line 218 of file otel.c.

218 {
219 stats_scope_set_observer(scope, stats_metric_record, NULL);
220}
void stats_scope_set_observer(stats_scope_t *scope, stats_observer_fn observer, void *context)

References stats_scope_set_observer().

Referenced by stats_runtime_start().

◆ ascii_otel_stats_metric()

ascii_otel_metric_t ascii_otel_stats_metric ( stats_record_kind_t kind,
unsigned id )

Definition at line 181 of file otel.c.

181 {
182 if (!((kind == STATS_RECORD_COUNTER && id < STATS_COUNTER_COUNT) ||
183 (kind == STATS_RECORD_GAUGE && id < STATS_GAUGE_COUNT) ||
186 ascii_otel_metric_t metric;
187 switch (((unsigned)kind << 16) | id) {
188#define ASCII_STATS_COUNTER(id, metric_id, name, unit) \
189 case (STATS_RECORD_COUNTER << 16) | STATS_COUNTER_##id: \
190 metric = ASCII_OTEL_METRIC_##metric_id; \
191 break;
192#define ASCII_STATS_GAUGE(id, metric_id, name, unit) \
193 case (STATS_RECORD_GAUGE << 16) | STATS_GAUGE_##id: \
194 metric = ASCII_OTEL_METRIC_##metric_id; \
195 break;
196#define ASCII_STATS_DURATION(id, metric_id, name, unit) \
197 case (STATS_RECORD_DURATION << 16) | STATS_DURATION_##id: \
198 metric = ASCII_OTEL_METRIC_##metric_id; \
199 break;
200#include <ascii-chat/stats/telemetry.def>
201#undef ASCII_STATS_COUNTER
202#undef ASCII_STATS_GAUGE
203#undef ASCII_STATS_DURATION
204 default:
206 }
207 return metric;
208}
ascii_otel_metric_t
Definition otel.h:12

References ASCII_OTEL_METRIC_COUNT, STATS_COUNTER_COUNT, STATS_DURATION_COUNT, STATS_GAUGE_COUNT, STATS_RECORD_COUNTER, STATS_RECORD_DURATION, and STATS_RECORD_GAUGE.

◆ ascii_otel_ui_usage()

void ascii_otel_ui_usage ( const char * screen,
const char * event,
uint64_t duration_ns )

Definition at line 434 of file otel.c.

434 {
435 // UI transitions must not inherit realtime media sampling decisions.
436 gotel_context_t root = {0};
437 ASCII_OTEL_SCOPE_PARENT(UI_USAGE, &root);
438 ASCII_OTEL_ATTR_STRING("ascii_chat.ui.screen", screen);
439 ASCII_OTEL_ATTR_STRING("ascii_chat.ui.event", event);
440 ASCII_OTEL_ATTR_INT("ascii_chat.ui.duration_ns", (int64_t)duration_ns);
441}

References ASCII_OTEL_ATTR_INT, ASCII_OTEL_ATTR_STRING, and ASCII_OTEL_SCOPE_PARENT.

Referenced by keyboard_help_toggle_terminal(), log_search_enter_mode(), log_search_exit_mode(), terminal_screen_logs_handle_key_sized(), ui_error_popup_key(), ui_input_media_take(), ui_input_read_key(), update_banner_set_result(), and update_banner_toggle_overlay().