• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TEN-framework / ten-framework / 20840308798

09 Jan 2026 03:35AM UTC coverage: 58.033%. First build
20840308798

Pull #1945

github

web-flow
Merge 165ccb0a6 into 0e991ca0b
Pull Request #1945: feat: support log with fields

486 of 824 new or added lines in 17 files covered. (58.98%)

55104 of 94953 relevant lines covered (58.03%)

815964.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.71
/core/src/ten_runtime/binding/python/native/ten_env/ten_env_log.c
1
//
2
// Copyright © 2025 Agora
3
// This file is part of TEN Framework, an open source project.
4
// Licensed under the Apache License, Version 2.0, with certain conditions.
5
// Refer to the "LICENSE" file in the root directory for more information.
6
//
7
#include <string.h>
8

9
#include "include_internal/ten_runtime/binding/python/common/error.h"
10
#include "include_internal/ten_runtime/binding/python/ten_env/ten_env.h"
11
#include "include_internal/ten_runtime/ten_env/log.h"
12
#include "ten_runtime/ten_env/internal/log.h"
13
#include "ten_utils/lib/error.h"
14
#include "ten_utils/macro/check.h"
15
#include "ten_utils/macro/memory.h"
16

17
typedef struct ten_env_notify_log_ctx_t {
18
  int32_t level;
19
  ten_string_t func_name;
20
  ten_string_t file_name;
21
  size_t line_no;
22
  ten_string_t msg;
23
  ten_string_t category;
24
  ten_event_t *completed;
25
  uint8_t *fields_buf;
26
  size_t fields_buf_size;
27
} ten_env_notify_log_ctx_t;
28

29
static ten_env_notify_log_ctx_t *ten_env_notify_log_ctx_create(
30
    int32_t level, const char *func_name, const char *file_name, size_t line_no,
31
    const char *msg, const char *category, bool sync, const uint8_t *fields_buf,
32
    size_t fields_buf_size) {
183✔
33
  ten_env_notify_log_ctx_t *ctx = TEN_MALLOC(sizeof(ten_env_notify_log_ctx_t));
183✔
34
  TEN_ASSERT(ctx, "Failed to allocate memory.");
183✔
35

36
  ctx->level = level;
183✔
37

38
  if (sync) {
183✔
39
    ctx->completed = ten_event_create(0, 1);
×
40
  } else {
183✔
41
    ctx->completed = NULL;
183✔
42
  }
183✔
43

44
  if (func_name) {
183✔
45
    ten_string_init_from_c_str_with_size(&ctx->func_name, func_name,
183✔
46
                                         strlen(func_name));
183✔
47
  } else {
183✔
48
    TEN_STRING_INIT(ctx->func_name);
×
49
  }
×
50

51
  if (file_name) {
183✔
52
    ten_string_init_from_c_str_with_size(&ctx->file_name, file_name,
183✔
53
                                         strlen(file_name));
183✔
54
  } else {
183✔
55
    TEN_STRING_INIT(ctx->file_name);
×
56
  }
×
57

58
  ctx->line_no = line_no;
183✔
59

60
  if (msg) {
183✔
61
    ten_string_init_from_c_str_with_size(&ctx->msg, msg, strlen(msg));
183✔
62
  } else {
183✔
63
    TEN_STRING_INIT(ctx->msg);
×
64
  }
×
65

66
  if (category) {
183✔
67
    ten_string_init_from_c_str_with_size(&ctx->category, category,
69✔
68
                                         strlen(category));
69✔
69
  } else {
114✔
70
    TEN_STRING_INIT(ctx->category);
114✔
71
  }
114✔
72

73
  // Copy fields buffer if provided
74
  if (fields_buf != NULL && fields_buf_size > 0) {
183✔
75
    ctx->fields_buf = TEN_MALLOC(fields_buf_size);
38✔
76
    TEN_ASSERT(ctx->fields_buf, "Failed to allocate memory for fields buffer.");
38✔
77
    memcpy(ctx->fields_buf, fields_buf, fields_buf_size);
38✔
78
    ctx->fields_buf_size = fields_buf_size;
38✔
79
  } else {
145✔
80
    ctx->fields_buf = NULL;
145✔
81
    ctx->fields_buf_size = 0;
145✔
82
  }
145✔
83

84
  return ctx;
183✔
85
}
183✔
86

87
static void ten_env_notify_log_ctx_destroy(ten_env_notify_log_ctx_t *ctx) {
183✔
88
  TEN_ASSERT(ctx, "Invalid argument.");
183✔
89

90
  ten_string_deinit(&ctx->func_name);
183✔
91
  ten_string_deinit(&ctx->file_name);
183✔
92
  ten_string_deinit(&ctx->msg);
183✔
93
  ten_string_deinit(&ctx->category);
183✔
94

95
  if (ctx->completed) {
183✔
96
    ten_event_destroy(ctx->completed);
×
97
    ctx->completed = NULL;
×
98
  }
×
99

100
  if (ctx->fields_buf != NULL) {
183✔
101
    TEN_FREE(ctx->fields_buf);
38✔
102
  }
38✔
103

104
  TEN_FREE(ctx);
183✔
105
}
183✔
106

107
static void ten_env_proxy_notify_log(ten_env_t *ten_env, void *user_data) {
156✔
108
  TEN_ASSERT(user_data, "Invalid argument.");
156✔
109
  TEN_ASSERT(ten_env, "Should not happen.");
156✔
110
  TEN_ASSERT(ten_env_check_integrity(ten_env, true), "Should not happen.");
156✔
111

112
  ten_env_notify_log_ctx_t *ctx = user_data;
156✔
113
  TEN_ASSERT(ctx, "Should not happen.");
156✔
114

115
  ten_env_log_with_fields_buf(
156✔
116
      ten_env, ctx->level, ten_string_get_raw_str(&ctx->func_name),
156✔
117
      ten_string_get_raw_str(&ctx->file_name), ctx->line_no,
156✔
118
      ten_string_get_raw_str(&ctx->msg), ten_string_get_raw_str(&ctx->category),
156✔
119
      ctx->fields_buf, ctx->fields_buf_size);
156✔
120

121
  if (ctx->completed) {
156✔
122
    ten_event_set(ctx->completed);
×
123
  } else {
156✔
124
    ten_env_notify_log_ctx_destroy(ctx);
156✔
125
  }
156✔
126
}
156✔
127

128
PyObject *ten_py_ten_env_log(PyObject *self, PyObject *args) {
183✔
129
  ten_py_ten_env_t *py_ten_env = (ten_py_ten_env_t *)self;
183✔
130
  TEN_ASSERT(py_ten_env && ten_py_ten_env_check_integrity(py_ten_env),
183✔
131
             "Invalid argument.");
183✔
132

133
  if (PyTuple_GET_SIZE(args) != 8) {
366✔
134
    return ten_py_raise_py_value_error_exception(
×
135
        "Invalid argument count when ten_env.log.");
×
136
  }
×
137

138
  TEN_LOG_LEVEL level = TEN_LOG_LEVEL_INVALID;
183✔
139
  const char *func_name = NULL;
183✔
140
  const char *file_name = NULL;
183✔
141
  size_t line_no = 0;
183✔
142
  const char *msg = NULL;
183✔
143
  const char *category = NULL;
183✔
144
  bool sync = false;
183✔
145
  PyObject *fields_buf_obj = NULL;
183✔
146

147
  if (!PyArg_ParseTuple(args, "izzizsbO", &level, &func_name, &file_name,
183✔
148
                        &line_no, &category, &msg, &sync, &fields_buf_obj)) {
183✔
149
    return ten_py_raise_py_value_error_exception(
×
150
        "Failed to parse argument when ten_env.log.");
×
151
  }
×
152

153
  ten_error_t err;
183✔
154
  TEN_ERROR_INIT(err);
183✔
155

156
  if (!py_ten_env->c_ten_env_proxy && !py_ten_env->c_ten_env) {
183✔
157
    ten_error_set(&err, TEN_ERROR_CODE_TEN_IS_CLOSED,
×
158
                  "ten_env.log() failed because ten is closed.");
×
159

160
    PyObject *result = (PyObject *)ten_py_error_wrap(&err);
×
161
    ten_error_deinit(&err);
×
162
    return result;
×
163
  }
×
164

165
  // Handle fields buffer
166
  const uint8_t *fields_buf = NULL;
183✔
167
  size_t fields_buf_size = 0;
183✔
168
  if (fields_buf_obj != NULL && fields_buf_obj != Py_None) {
183✔
169
    if (!PyBytes_Check(fields_buf_obj)) {
38✔
NEW
170
      return ten_py_raise_py_value_error_exception(
×
NEW
171
          "fields_buf must be bytes or None.");
×
NEW
172
    }
×
173
    fields_buf = (const uint8_t *)PyBytes_AS_STRING(fields_buf_obj);
38✔
174
    fields_buf_size = PyBytes_GET_SIZE(fields_buf_obj);
38✔
175
  }
38✔
176

177
  ten_env_notify_log_ctx_t *ctx = ten_env_notify_log_ctx_create(
183✔
178
      level, func_name, file_name, line_no, msg, category, sync, fields_buf,
183✔
179
      fields_buf_size);
183✔
180

181
  if (py_ten_env->c_ten_env_proxy) {
183✔
182
    if (!ten_env_proxy_notify(py_ten_env->c_ten_env_proxy,
156✔
183
                              ten_env_proxy_notify_log, ctx, false, &err)) {
156✔
184
      PyObject *result = (PyObject *)ten_py_error_wrap(&err);
×
185
      ten_error_deinit(&err);
×
186
      ten_env_notify_log_ctx_destroy(ctx);
×
187
      return result;
×
188
    }
×
189

190
    if (sync) {
156✔
191
      ten_event_wait(ctx->completed, -1);
×
192
      ten_env_notify_log_ctx_destroy(ctx);
×
193
    }
×
194
  } else {
156✔
195
    // TODO(Wei): This function is currently specifically designed for the addon
196
    // because the addon currently does not have a main thread, so it's unable
197
    // to use the ten_env_proxy mechanism to maintain thread safety. Once the
198
    // main thread for the addon is determined in the future, these hacks made
199
    // specifically for the addon can be completely removed, and comprehensive
200
    // thread safety mechanism can be implemented.
201
    TEN_ASSERT(py_ten_env->c_ten_env->attach_to == TEN_ENV_ATTACH_TO_ADDON,
27✔
202
               "Should not happen.");
27✔
203

204
    ten_env_log_without_check_thread(py_ten_env->c_ten_env, ctx->level,
27✔
205
                                     ten_string_get_raw_str(&ctx->func_name),
27✔
206
                                     ten_string_get_raw_str(&ctx->file_name),
27✔
207
                                     ctx->line_no,
27✔
208
                                     ten_string_get_raw_str(&ctx->msg),
27✔
209
                                     ten_string_get_raw_str(&ctx->category),
27✔
210
                                     ctx->fields_buf, ctx->fields_buf_size);
27✔
211

212
    ten_env_notify_log_ctx_destroy(ctx);
27✔
213
  }
27✔
214

215
  ten_error_deinit(&err);
183✔
216

217
  Py_RETURN_NONE;
183✔
218
}
183✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc