• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# lws logging
2
3# `lwsl_` logging apis
4
5LWS has traditionally provided logging arrangements that are not indirected
6through the lws context, because logging may be needed before and after the
7context existence.  For that reason the original logging arrangements are
8processwide.
9
10By default the logs are emitted on stdout, but this can be overridden
11using `lws_set_log_level()` and either syslog (provided by `lwsl_emit_syslog()`)
12or custom log emission is possible if you point it to your own.
13
14Currently the following log levels are defined
15
16|name|function|release|meaning|
17|---|---|---|---|
18|`LLL_ERR`|`lwsl_err()`|y|Serious operation errors anyone needs to know|
19|`LLL_WARN`|`lwsl_warn()`|y|Operation errors you may need to know|
20|`LLL_USER`|`lws_user()`|y|Information user code wants you to know|
21|`LLL_NOTICE`|`lwsl_notice()`|y|Information about what lws is doing useful for logging|
22|`LLL_INFO`|`lwsl_info()`|n|Detailed information about what lws is doing|
23|`LLL_DEBUG`|`lwsl_debug()`|n|Very detailed information about what lws is doing|
24|`LLL_PARSER`|`lwsl_parser()`|n|Very detailed information about parsing|
25|`LLL_HEADER`|`lwsl_header()`|n|Very detailed information about header processing|
26|`LLL_EXT`|`lwsl_ext()`|n|Very detailed information about ws extensions|
27|`LLL_CLIENT`|`lwsl_client()`|n|Very detailed information about client connections|
28|`LLL_LATENCY`|`lwsl_latency()`|n|detailed latency stats|
29|`LLL_THREAD`|`lwsl_thread()`|n|detailed threadpool information|
30
31The first four log levels are built into lws even on Release builds, the others
32are only built in Debug builds.
33
34You can select between Debug and Release builds using cmake `-DCMAKE_BUILD_TYPE=`
35`DEBUG` or `Release`
36
37`lws_set_log_level()` is used to OR together the logging bitfields you want to
38see emitted, only log levels that were built in can be enabled since the code for them
39is just not there otherwise.
40
41## Finegrained control of log level build
42
43You can deviate from the default log inclusion for release / debug by overriding it
44at cmake, using `LWS_LOGGING_BITFIELD_SET` and `LWS_LOGGING_BITFIELD_CLEAR`.
45
46For example you can set `-DLWS_LOGGING_BITFIELD_SET="LLL_INFO|LLL_DEBUG"`, which will
47cause those log level traces to be built in even in Release mode.  Clear works
48similarly to defeat build of specific log levels.
49
50## Object tags in lws
51
52Commonly logging wants to refer to an object in a repeatable way, the usual way to
53do this is with `%p` to print the object pointer.  But this has a couple of drawbacks,
54first the same memory may be freed and reallocated for a different instance of the same
55or another object, causing confusion, and second when multiple processes are allocating
56objects and logging, the same address may be allocated in different process also causing
57confusion.
58
59Lws has introduced unique tag strings to refer to object identity in logging instead, these
60contain various information such as a 64-bit ordinal for the group the object belongs
61to that won't repeat even if reallocated to the same address (until 2^64 allocations,
62anyway).
63
64Tags are fixed at object creation time for the whole object lifetime, although in some
65cases the tag may be appended to... accepted server wsis for example don't have much
66information available to form the tag until they start to indicate what they want to
67do.
68
69At their simplest the tags look like this (in a log indicating creation)
70
71```
72[2020/12/27 08:49:19:2956] N:  ++ (4) [wsi|5|h2]
73```
74
75It means a wsi has been created with the tag `[wsi|5|h2]`, and after that, there are 4
76active objects in the wsi group.
77
78The corresponding object destruction log with the tag is
79
80```
81[2020/12/27 08:49:24:4226] N:  -- (3)   5.126s [wsi|5|h2]
82```
83
84it indicates the object's tag, that it lived for 5.126s and after its destruction,
85there are 3 objects in its group left.
86
87### Compound tags
88
89If the object has bindings, the tag can reflect that, eg
90
91```
92[2020/12/27 08:49:19:4787] N:  ++ (2) [wsiSScli|6|d_h1]
93[2020/12/27 08:49:19:4793] N:  ++ (2) [wsicli|6|GET/h1/httpbin.org/([wsiSScli|6|d_h1])]
94```
95
96the first log is describing a proxied SS client connection at the proxy, and the second
97is a wsi bound to the SS object from the first log to do the outgoing client action.
98
99## Tags in user code
100
101When user code wants to refer to a tagged object like a wsi or vhost, there are helpers
102that return a `const char *` containing the tag
103
104|tag accessors|
105|---|
106|`lws_wsi_tag(wsi)`|
107|`lws_vh_tag(vh)`|
108|`lws_ss_tag(h)`|
109
110# New logging context apis
111
112From v4.3 on lws additionally provides wrappers that issue logs into a
113"log context" object, one of these is embedded in the lws_context, lws_vhost,
114wsi, ss and sspc handles.  These follow the same general approach as before, but
115allow logs to be issued in "the context" of any of those objects, and to fall
116back sanely if the object pointer is NULL.
117
118The traditional process scope logs and emit management remain available as
119before, and if you do not set custom log contexts, the new log apis use the
120processwide log context emit and mask as before too.
121
122Here's a summary of the differences:
123
124|Traditional process scope logs|New log context apis|
125|---|---|
126|Single processwide log context|Defaults to processwide, but object can use custom log contexts|
127|Single processwide emit function|Emit function per log context|
128|Single processwide log mask|log mask is in log context, objects can be bound to custom log contexts at creation time|
129|Require trailing `\n` in format|Trailing `\n` added if not present|
130|Manual `__func__`|`__func__` added in wrapper macros automatically|
131|Manual tag addition|Object tag prepended automatically|
132|No hierarchy|Log contexts may refer to parent log contexts, which may prepend to child logs|
133|Macros per level (eg, `lwsl_err(...)`)|Macros per object type / level (eg, `lwsl_wsi_err(wsi, ...)`)|
134
135In addition to being able to control the emit function and log level for
136individual log contexts, eg, for a particular wsi, the log functions understand
137how to prepend object-specific information such as tags and `__func__`
138automatically.  They also do not need a trailing `\n` in the format string.  So
139the new context aware logs remove boilerplate from the logging calls while
140making the log information more consistent.
141
142So comparing this kind of logging the processwide and log context aware ways:
143
144```
145[2021/06/25 09:39:34:7050] N: [669282|wsicli|4|GET/h1/libwebsockets.org|default]: _lws_generic_transaction_completed_active_conn:  ...
146```
147
148|Type|Example code|
149|---|---|
150|Process scope apis|`lwsl_notice("%s: %s: mylog %d\n", __func__, lws_wsi_tag(wsi), n);`|
151|New log context apis|`lwsl_wsi_notice(wsi, "mylog %d", n);`|
152
153The log context / object-aware apis do not replace the processwide logging but
154augment it, and the new apis default to use the original processwide emit
155function and log mask, so the behaviours are the same.  The original processwide
156log apis themselves are unchanged.
157
158At lws_context creation time, you can set the context info `.log_cx` to a user
159defined log context which is inherited by objects created in that lws_context by
160default.  Vhost creation, wsi creation and ss / sspc creation all allow passing
161a user log_cx to customize how logs for that object are handled.
162
163## Using the new logging apis
164
165This table describes the different ways to issue an ERROR verbosity log, it
166works the same for info, notice, warn, etc.
167
168|Scope|Api example|Functionality|
169|---|---|---|
170|Old, Processwide|lwsl_err(...)|Traditional processwide error log|
171|lws_context|lwsl_cx_err(context, ...)|error log bound to lws_context|
172|lws_vhost|lwsl_vhost_err(vh, ...)|error log bound to lws_vhost|
173|lws_wsi|lwsl_wsi_err(wsi, ...)|error log bound to wsi|
174|lws_ss|lwsl_ss_err(handle, ...)|error log bound to secure stream|
175
176Similarly hexdumps can be bound to different log contexts
177
178|Scope|Api example|Functionality|
179|---|---|---|
180|Old, Processwide|lwsl_hexdump_err(...)|Traditional processwide error hexdump|
181|lws_context|lwsl_hexdump_cx_err(context, ...)|error hexdump bound to lws_context|
182|lws_vhost|lwsl_hexdump_vhost_err(vh, ...)|error hexdump bound to lws_vhost|
183|lws_wsi|lwsl_hexdump_wsi_err(wsi, ...)|error hexdump bound to wsi|
184|lws_ss|lwsl_hexdump_ss_err(handle, ...)|error hexdump bound to secure stream|
185
186## Creating and using custom log contexts
187
188The log context object is public, in `libwebsockets/lws-logs.h`, currently it
189is like this
190
191```
192typedef void (*lws_log_emit_t)(int level, const char *line);
193typedef void (*lws_log_emit_cx_t)(struct lws_log_cx *cx, int level,
194				  const char *line, size_t len);
195typedef void (*lws_log_prepend_cx_t)(struct lws_log_cx *cx, void *obj,
196				     char **p, char *e);
197typedef void (*lws_log_use_cx_t)(struct lws_log_cx *cx, int _new);
198typedef struct lws_log_cx {
199	union {
200		lws_log_emit_t		emit; /* legacy emit function */
201		lws_log_emit_cx_t	emit_cx; /* LLLF_LOG_CONTEXT_AWARE */
202	} u;
203	lws_log_use_cx_t		refcount_cb;
204	/**< NULL, or a function called after each change to .refcount below,
205	 * this enables implementing side-effects like opening and closing
206	 * log files when the first and last object binds / unbinds */
207	lws_log_prepend_cx_t		prepend;
208	/**< NULL, or a cb to optionally prepend a string to logs we are a
209	 * parent of */
210	struct lws_log_cx		*parent;
211	/**< NULL, or points to log ctx we are a child of */
212	void				*opaque;
213	/**< ignored by lws, used to pass config to emit_cx, eg, filepath */
214	void				*stg;
215	/**< ignored by lws, may be used a storage by refcount_cb / emit_cx */
216	uint32_t			lll_flags;
217	/**< mask of log levels we want to emit in this context */
218	int32_t				refcount;
219	/**< refcount of objects bound to this log context */
220} lws_log_cx_t;
221```
222
223The emit function is a union because the traditional logs and the old emit
224functions are also implemented using the new log contexts internally.  For
225new log context-aware code, you would use `.u.emit_cx` and set the flag
226`LLLF_LOG_CONTEXT_AWARE` on `.lll_flags`.
227
228Lws also exports some common emit and refcount functions so you don't have to
229reinvent the wheel
230
231|Dest|emit member|`.lll_flags`|emit|`.refcount_cb`|`.opaque`|
232|---|---|---|---|---|---|
233|stderr|`.u.emit`|-|`lwsl_emit_stderr`|NULL|NULL|
234|file|`.u.emit_cx`|`LLLF_LOG_CONTEXT_AWARE`|`lws_log_emit_cx_file`|`lws_log_use_cx_file`|`(const char *)filepath`|
235
236For example, a custom log context that emits to a configurable file can be
237declared like this (lws exports the needed helpers already)
238
239```
240static lws_log_cx_t my_log_cx = {
241        .lll_flags      = LLLF_LOG_CONTEXT_AWARE |
242                          LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER,
243        .refcount_cb    = lws_log_use_cx_file,
244        .u.emit_cx      = lws_log_emit_cx_file,
245        .opaque	        = "/tmp/mylogpath.log" /* also settable at runtime */
246};
247```
248
249To bind the lws_context to this log context, set `log_cx` in the context
250creation info struct
251
252```
253	info.log_cx = &my_log_cx;
254```
255
256### Log context hierarchy
257
258Log contexts may also point to a parent log context... the top level log context
259defines the emit function to be used, but parent log contexts are consulted by
260calling their prepend function if any, to annotate logs with information from
261parent levels.
262
263### Log context prepend function
264
265Logs contexts may define a "prepend" function callback, that knows how to
266represent the object in a brief string to be prepended to other logs.  For
267example the wsi-aware log context layer knows how to provide the wsi tag
268when called.
269
270Prepend functions should add `:<space>` after their output, if any, since these
271will appear before the start of other logs.
272
273### Log context opaque member
274
275The `.opaque` member is available for passing in configuration to the emit and
276refcount_cb members.  Lws does not use this itself at all.
277
278### Log context refcounting
279
280An expected use for custom log contexts is emitting to a specific file, and
281then binding one or more objects to that log context.  Since it's too expensive
282to keep opening and closing the output file per log, it means we need to know
283when we bind to the first object and unbind from the last, so we can keep the
284file handle open.
285
286For this reason the log contexts have a refcount, and an opaque `void *stg`
287availble for the emit and refounct_cb to use how they see fit, eg, for storing
288the output log file descriptor.
289