1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 /***************************************************************************
25  Log.cc
26 
27  This file defines the implementation of the static Log class, which is
28  primarily used as a namespace.  That is, there are no Log objects, but the
29  class scope and static members provide a protected namespace for all of
30  the logging routines and enumerated types.  When C++ namespaces are more
31  widely-implemented, Log could be implemented as a namespace rather than a
32  class.
33 
34  ***************************************************************************/
35 #include "tscore/ink_platform.h"
36 #include "tscore/TSSystemState.h"
37 #include "P_EventSystem.h"
38 #include "P_Net.h"
39 #include "I_Machine.h"
40 #include "HTTP.h"
41 
42 #include "LogAccess.h"
43 #include "LogField.h"
44 #include "LogFilter.h"
45 #include "LogFormat.h"
46 #include "LogFile.h"
47 #include "LogObject.h"
48 #include "LogConfig.h"
49 #include "LogBuffer.h"
50 #include "LogUtils.h"
51 #include "Log.h"
52 #include "tscore/SimpleTokenizer.h"
53 
54 #include "tscore/ink_apidefs.h"
55 
56 #define PERIODIC_TASKS_INTERVAL_FALLBACK 5
57 
58 // Log global objects
59 inkcoreapi LogObject *Log::error_log = nullptr;
60 LogFieldList Log::global_field_list;
61 Log::LoggingMode Log::logging_mode = LOG_MODE_NONE;
62 
63 // Flush thread stuff
64 EventNotify *Log::preproc_notify;
65 EventNotify *Log::flush_notify;
66 InkAtomicList *Log::flush_data_list;
67 
68 // Log private objects
69 int Log::preproc_threads;
70 int Log::init_status                  = 0;
71 int Log::config_flags                 = 0;
72 bool Log::logging_mode_changed        = false;
73 bool Log::log_rotate_signal_received  = false;
74 uint32_t Log::periodic_tasks_interval = PERIODIC_TASKS_INTERVAL_FALLBACK;
75 
76 // Hash table for LogField symbols
77 std::unordered_map<std::string, LogField *> Log::field_symbol_hash;
78 
79 RecRawStatBlock *log_rsb;
80 
81 /*-------------------------------------------------------------------------
82   Log::change_configuration
83 
84   This routine is invoked when the current LogConfig object says it needs
85   to be changed (as the result of a manager callback).
86   -------------------------------------------------------------------------*/
87 
88 LogConfig *Log::config       = nullptr;
89 static unsigned log_configid = 0;
90 
91 // Downcast from a Ptr<LogFieldAliasTable> to a Ptr<LogFieldAliasMap>.
92 static Ptr<LogFieldAliasMap>
make_alias_map(Ptr<LogFieldAliasTable> & table)93 make_alias_map(Ptr<LogFieldAliasTable> &table)
94 {
95   return make_ptr(static_cast<LogFieldAliasMap *>(table.get()));
96 }
97 
98 void
change_configuration()99 Log::change_configuration()
100 {
101   LogConfig *prev_config = Log::config;
102   LogConfig *new_config  = nullptr;
103 
104   Debug("log-config", "Changing configuration ...");
105 
106   new_config = new LogConfig;
107   ink_assert(new_config != nullptr);
108   new_config->read_configuration_variables();
109 
110   // grab the _APImutex so we can transfer the api objects to
111   // the new config
112   //
113   ink_mutex_acquire(prev_config->log_object_manager._APImutex);
114   Debug("log-api-mutex", "Log::change_configuration acquired api mutex");
115 
116   new_config->init(prev_config);
117 
118   // Make the new LogConfig active.
119   ink_atomic_swap(&Log::config, new_config);
120 
121   // XXX There is a race condition with API objects. If TSTextLogObjectCreate()
122   // is called before the Log::config swap, then it will be blocked on the lock
123   // on the *old* LogConfig and register it's LogObject with that manager. If
124   // this happens, then the new TextLogObject will be immediately lost. Traffic
125   // Server would crash the next time the plugin referenced the freed object.
126 
127   ink_mutex_release(prev_config->log_object_manager._APImutex);
128   Debug("log-api-mutex", "Log::change_configuration released api mutex");
129 
130   // Register the new config in the config processor; the old one will now be scheduled for a
131   // future deletion. We don't need to do anything magical with refcounts, since the
132   // configProcessor will keep a reference count, and drop it when the deletion is scheduled.
133   configProcessor.set(log_configid, new_config);
134 
135   // If we replaced the logging configuration, flush any log
136   // objects that weren't transferred to the new config ...
137   prev_config->log_object_manager.flush_all_objects();
138 
139   Debug("log-config", "... new configuration in place");
140 }
141 
142 /*-------------------------------------------------------------------------
143   PERIODIC EVENTS
144 
145   There are a number of things that need to get done on a periodic basis,
146   such as checking the amount of space used, seeing if it's time to roll
147   files, and flushing idle log buffers.  Most of these tasks require having
148   exclusive access to the back-end structures, which is controlled by the
149   flush_thread.  Therefore, we will simply instruct the flush thread to
150   execute a periodic_tasks() function once per period.  To ensure that the
151   tasks are executed AT LEAST once each period, we'll register a call-back
152   with the system and trigger the flush thread's condition variable.  To
153   ensure that the tasks are executed AT MOST once per period, the flush
154   thread will keep track of executions per period.
155   -------------------------------------------------------------------------*/
156 
157 /*-------------------------------------------------------------------------
158   PeriodicWakeup
159 
160   This continuation is invoked each second to wake-up the flush thread,
161   just in case it's sleeping on the job.
162   -------------------------------------------------------------------------*/
163 
164 struct PeriodicWakeup;
165 using PeriodicWakeupHandler = int (PeriodicWakeup::*)(int, void *);
166 struct PeriodicWakeup : Continuation {
167   int m_preproc_threads;
168   int m_flush_threads;
169 
170   int
wakeupPeriodicWakeup171   wakeup(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
172   {
173     for (int i = 0; i < m_preproc_threads; i++) {
174       Log::preproc_notify[i].signal();
175     }
176     for (int i = 0; i < m_flush_threads; i++) {
177       Log::flush_notify[i].signal();
178     }
179     return EVENT_CONT;
180   }
181 
PeriodicWakeupPeriodicWakeup182   PeriodicWakeup(int preproc_threads, int flush_threads)
183     : Continuation(new_ProxyMutex()), m_preproc_threads(preproc_threads), m_flush_threads(flush_threads)
184   {
185     SET_HANDLER((PeriodicWakeupHandler)&PeriodicWakeup::wakeup);
186   }
187 };
188 
189 /*-------------------------------------------------------------------------
190   Log::periodic_tasks
191 
192   This function contains all of the tasks that need to be done each
193   PERIODIC_TASKS_INTERVAL seconds.
194   -------------------------------------------------------------------------*/
195 
196 void
periodic_tasks(long time_now)197 Log::periodic_tasks(long time_now)
198 {
199   Debug("log-api-mutex", "entering Log::periodic_tasks");
200 
201   if (logging_mode_changed || Log::config->reconfiguration_needed) {
202     Debug("log-config", "Performing reconfiguration, init status = %d", init_status);
203 
204     if (logging_mode_changed) {
205       int val = static_cast<int>(REC_ConfigReadInteger("proxy.config.log.logging_enabled"));
206 
207       if (val < LOG_MODE_NONE || val > LOG_MODE_FULL) {
208         logging_mode = LOG_MODE_FULL;
209         Warning("proxy.config.log.logging_enabled has an invalid "
210                 "value setting it to %d",
211                 logging_mode);
212       } else {
213         logging_mode = static_cast<LoggingMode>(val);
214       }
215       logging_mode_changed = false;
216     }
217     // even if we are disabling logging, we call change configuration
218     // so that log objects are flushed
219     //
220     change_configuration();
221   } else if (logging_mode > LOG_MODE_NONE || config->has_api_objects()) {
222     Debug("log-periodic", "Performing periodic tasks");
223     Debug("log-periodic", "Periodic task interval = %d", periodic_tasks_interval);
224 
225     // Check if space is ok and update the space used
226     //
227     if (config->space_is_short() || time_now % config->space_used_frequency == 0) {
228       Log::config->update_space_used();
229     }
230 
231     // See if there are any buffers that have expired
232     //
233     Log::config->log_object_manager.check_buffer_expiration(time_now);
234 
235     // Check if we received a request to roll, and roll if so, otherwise
236     // give objects a chance to roll if they need to
237     //
238     if (Log::config->roll_log_files_now) {
239       if (error_log) {
240         error_log->roll_files(time_now);
241       }
242       Log::config->log_object_manager.roll_files(time_now);
243       Log::config->roll_log_files_now = false;
244     } else {
245       if (error_log) {
246         error_log->roll_files(time_now);
247       }
248       Log::config->log_object_manager.roll_files(time_now);
249     }
250     if (log_rotate_signal_received) {
251       Log::config->log_object_manager.reopen_moved_log_files();
252       log_rotate_signal_received = false;
253     }
254   }
255 }
256 
257 /*-------------------------------------------------------------------------
258   MAIN INTERFACE
259   -------------------------------------------------------------------------*/
260 struct LoggingPreprocContinuation : public Continuation {
261   int m_idx;
262 
263   int
mainEventLoggingPreprocContinuation264   mainEvent(int /* event ATS_UNUSED */, void * /* data ATS_UNUSED */)
265   {
266     Log::preproc_thread_main((void *)&m_idx);
267     return 0;
268   }
269 
LoggingPreprocContinuationLoggingPreprocContinuation270   explicit LoggingPreprocContinuation(int idx) : Continuation(nullptr), m_idx(idx)
271   {
272     SET_HANDLER(&LoggingPreprocContinuation::mainEvent);
273   }
274 };
275 
276 struct LoggingFlushContinuation : public Continuation {
277   int m_idx;
278 
279   int
mainEventLoggingFlushContinuation280   mainEvent(int /* event ATS_UNUSED */, void * /* data ATS_UNUSED */)
281   {
282     Log::flush_thread_main((void *)&m_idx);
283     return 0;
284   }
285 
LoggingFlushContinuationLoggingFlushContinuation286   explicit LoggingFlushContinuation(int idx) : Continuation(nullptr), m_idx(idx)
287   {
288     SET_HANDLER(&LoggingFlushContinuation::mainEvent);
289   }
290 };
291 
292 /*-------------------------------------------------------------------------
293   Log::init_fields
294 
295   Define the available logging fields.
296   This used to be part of the init() function, but now is separate so that
297   standalone programs that do not require more services (e.g., that do not
298   need to read records.config) can just call init_fields.
299 
300   Note that the LogFields are added to the list with the copy flag false so
301   that the LogFieldList destructor will reclaim this memory.
302   -------------------------------------------------------------------------*/
303 void
init_fields()304 Log::init_fields()
305 {
306   if (init_status & FIELDS_INITIALIZED) {
307     return;
308   }
309 
310   LogField *field;
311 
312   //
313   // Initializes material to find a milestone name from their
314   // name in a rapid manner.
315   LogField::init_milestone_container();
316 
317   // client -> proxy fields
318   field = new LogField("client_host_ip", "chi", LogField::IP, &LogAccess::marshal_client_host_ip, &LogAccess::unmarshal_ip_to_str);
319   global_field_list.add(field, false);
320   field_symbol_hash.emplace("chi", field);
321 
322   field =
323     new LogField("client_host_port", "chp", LogField::sINT, &LogAccess::marshal_client_host_port, &LogAccess::unmarshal_int_to_str);
324   global_field_list.add(field, false);
325   field_symbol_hash.emplace("chp", field);
326 
327   field =
328     new LogField("client_host_ip_hex", "chih", LogField::IP, &LogAccess::marshal_client_host_ip, &LogAccess::unmarshal_ip_to_hex);
329   global_field_list.add(field, false);
330   field_symbol_hash.emplace("chih", field);
331 
332   // interface ip
333 
334   field =
335     new LogField("host_interface_ip", "hii", LogField::IP, &LogAccess::marshal_host_interface_ip, &LogAccess::unmarshal_ip_to_str);
336   global_field_list.add(field, false);
337   field_symbol_hash.emplace("hii", field);
338 
339   field = new LogField("host_interface_ip_hex", "hiih", LogField::IP, &LogAccess::marshal_host_interface_ip,
340                        &LogAccess::unmarshal_ip_to_hex);
341   global_field_list.add(field, false);
342   field_symbol_hash.emplace("hiih", field);
343   // interface ip end
344   field = new LogField("client_auth_user_name", "caun", LogField::STRING, &LogAccess::marshal_client_auth_user_name,
345                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
346   global_field_list.add(field, false);
347   field_symbol_hash.emplace("caun", field);
348 
349   field = new LogField("plugin_identity_id", "piid", LogField::sINT, &LogAccess::marshal_plugin_identity_id,
350                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_int_to_str));
351   global_field_list.add(field, false);
352   field_symbol_hash.emplace("piid", field);
353 
354   field = new LogField("plugin_identity_tag", "pitag", LogField::STRING, &LogAccess::marshal_plugin_identity_tag,
355                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
356   global_field_list.add(field, false);
357   field_symbol_hash.emplace("pitag", field);
358 
359   field = new LogField("client_req_timestamp_sec", "cqts", LogField::sINT, &LogAccess::marshal_client_req_timestamp_sec,
360                        &LogAccess::unmarshal_int_to_str);
361   global_field_list.add(field, false);
362   field_symbol_hash.emplace("cqts", field);
363 
364   field = new LogField("client_req_timestamp_hex_sec", "cqth", LogField::sINT, &LogAccess::marshal_client_req_timestamp_sec,
365                        &LogAccess::unmarshal_int_to_str_hex);
366   global_field_list.add(field, false);
367   field_symbol_hash.emplace("cqth", field);
368 
369   field = new LogField("client_req_timestamp_squid", "cqtq", LogField::sINT, &LogAccess::marshal_client_req_timestamp_ms,
370                        &LogAccess::unmarshal_ttmsf);
371   global_field_list.add(field, false);
372   field_symbol_hash.emplace("cqtq", field);
373 
374   field = new LogField("client_req_timestamp_netscape", "cqtn", LogField::sINT, &LogAccess::marshal_client_req_timestamp_sec,
375                        &LogAccess::unmarshal_int_to_netscape_str);
376   global_field_list.add(field, false);
377   field_symbol_hash.emplace("cqtn", field);
378 
379   field = new LogField("client_req_timestamp_date", "cqtd", LogField::sINT, &LogAccess::marshal_client_req_timestamp_sec,
380                        &LogAccess::unmarshal_int_to_date_str);
381   global_field_list.add(field, false);
382   field_symbol_hash.emplace("cqtd", field);
383 
384   field = new LogField("client_req_timestamp_time", "cqtt", LogField::sINT, &LogAccess::marshal_client_req_timestamp_sec,
385                        &LogAccess::unmarshal_int_to_time_str);
386   global_field_list.add(field, false);
387   field_symbol_hash.emplace("cqtt", field);
388 
389   field = new LogField("client_req_text", "cqtx", LogField::STRING, &LogAccess::marshal_client_req_text,
390                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_http_text));
391   global_field_list.add(field, false);
392   field_symbol_hash.emplace("cqtx", field);
393 
394   field = new LogField("client_req_http_method", "cqhm", LogField::STRING, &LogAccess::marshal_client_req_http_method,
395                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
396   global_field_list.add(field, false);
397   field_symbol_hash.emplace("cqhm", field);
398 
399   field = new LogField("client_req_url", "cqu", LogField::STRING, &LogAccess::marshal_client_req_url,
400                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str), &LogAccess::set_client_req_url);
401   global_field_list.add(field, false);
402   field_symbol_hash.emplace("cqu", field);
403 
404   field = new LogField("client_req_url_canonical", "cquc", LogField::STRING, &LogAccess::marshal_client_req_url_canon,
405                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str), &LogAccess::set_client_req_url_canon);
406   global_field_list.add(field, false);
407   field_symbol_hash.emplace("cquc", field);
408 
409   field = new LogField(
410     "client_req_unmapped_url_canonical", "cquuc", LogField::STRING, &LogAccess::marshal_client_req_unmapped_url_canon,
411     reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str), &LogAccess::set_client_req_unmapped_url_canon);
412   global_field_list.add(field, false);
413   field_symbol_hash.emplace("cquuc", field);
414 
415   field = new LogField("client_req_unmapped_url_path", "cquup", LogField::STRING, &LogAccess::marshal_client_req_unmapped_url_path,
416                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str),
417                        &LogAccess::set_client_req_unmapped_url_path);
418   global_field_list.add(field, false);
419   field_symbol_hash.emplace("cquup", field);
420 
421   field = new LogField("client_req_unmapped_url_host", "cquuh", LogField::STRING, &LogAccess::marshal_client_req_unmapped_url_host,
422                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str),
423                        &LogAccess::set_client_req_unmapped_url_host);
424   global_field_list.add(field, false);
425   field_symbol_hash.emplace("cquuh", field);
426 
427   field = new LogField("client_req_url_scheme", "cqus", LogField::STRING, &LogAccess::marshal_client_req_url_scheme,
428                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
429   global_field_list.add(field, false);
430   field_symbol_hash.emplace("cqus", field);
431 
432   field = new LogField("client_req_url_path", "cqup", LogField::STRING, &LogAccess::marshal_client_req_url_path,
433                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str), &LogAccess::set_client_req_url_path);
434   global_field_list.add(field, false);
435   field_symbol_hash.emplace("cqup", field);
436 
437   field = new LogField("client_req_http_version", "cqhv", LogField::dINT, &LogAccess::marshal_client_req_http_version,
438                        &LogAccess::unmarshal_http_version);
439   global_field_list.add(field, false);
440   field_symbol_hash.emplace("cqhv", field);
441 
442   field = new LogField("client_req_protocol_version", "cqpv", LogField::dINT, &LogAccess::marshal_client_req_protocol_version,
443                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
444   global_field_list.add(field, false);
445   field_symbol_hash.emplace("cqpv", field);
446 
447   field = new LogField("server_req_protocol_version", "sqpv", LogField::dINT, &LogAccess::marshal_server_req_protocol_version,
448                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
449   global_field_list.add(field, false);
450   field_symbol_hash.emplace("sqpv", field);
451 
452   field = new LogField("client_req_header_len", "cqhl", LogField::sINT, &LogAccess::marshal_client_req_header_len,
453                        &LogAccess::unmarshal_int_to_str);
454   global_field_list.add(field, false);
455   field_symbol_hash.emplace("cqhl", field);
456 
457   field = new LogField("client_req_squid_len", "cqql", LogField::sINT, &LogAccess::marshal_client_req_squid_len,
458                        &LogAccess::unmarshal_int_to_str);
459   global_field_list.add(field, false);
460   field_symbol_hash.emplace("cqql", field);
461 
462   field = new LogField("cache_lookup_url_canonical", "cluc", LogField::STRING, &LogAccess::marshal_cache_lookup_url_canon,
463                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
464   global_field_list.add(field, false);
465   field_symbol_hash.emplace("cluc", field);
466 
467   field = new LogField("client_sni_server_name", "cssn", LogField::STRING, &LogAccess::marshal_client_sni_server_name,
468                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
469   global_field_list.add(field, false);
470   field_symbol_hash.emplace("cssn", field);
471 
472   field = new LogField("client_ssl_cert_provided", "cscert", LogField::STRING, &LogAccess::marshal_client_provided_cert,
473                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_int_to_str));
474   global_field_list.add(field, false);
475   field_symbol_hash.emplace("cscert", field);
476 
477   field = new LogField("proxy_ssl_cert_provided", "pscert", LogField::STRING, &LogAccess::marshal_proxy_provided_cert,
478                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_int_to_str));
479   global_field_list.add(field, false);
480   field_symbol_hash.emplace("pscert", field);
481 
482   field = new LogField("process_uuid", "puuid", LogField::STRING, &LogAccess::marshal_process_uuid,
483                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
484   global_field_list.add(field, false);
485   field_symbol_hash.emplace("puuid", field);
486 
487   field = new LogField("client_req_content_len", "cqcl", LogField::sINT, &LogAccess::marshal_client_req_content_len,
488                        &LogAccess::unmarshal_int_to_str);
489   global_field_list.add(field, false);
490   field_symbol_hash.emplace("cqcl", field);
491 
492   field = new LogField("client_req_tcp_reused", "cqtr", LogField::dINT, &LogAccess::marshal_client_req_tcp_reused,
493                        &LogAccess::unmarshal_int_to_str);
494   global_field_list.add(field, false);
495   field_symbol_hash.emplace("cqtr", field);
496 
497   field = new LogField("client_req_is_ssl", "cqssl", LogField::dINT, &LogAccess::marshal_client_req_is_ssl,
498                        &LogAccess::unmarshal_int_to_str);
499   global_field_list.add(field, false);
500   field_symbol_hash.emplace("cqssl", field);
501 
502   field = new LogField("client_req_ssl_reused", "cqssr", LogField::dINT, &LogAccess::marshal_client_req_ssl_reused,
503                        &LogAccess::unmarshal_int_to_str);
504   global_field_list.add(field, false);
505   field_symbol_hash.emplace("cqssr", field);
506 
507   field = new LogField("client_req_is_internal", "cqint", LogField::sINT, &LogAccess::marshal_client_req_is_internal,
508                        &LogAccess::unmarshal_int_to_str);
509   global_field_list.add(field, false);
510   field_symbol_hash.emplace("cqint", field);
511 
512   field = new LogField("client_req_mptcp", "cqmpt", LogField::sINT, &LogAccess::marshal_client_req_mptcp_state,
513                        &LogAccess::unmarshal_int_to_str);
514   global_field_list.add(field, false);
515   field_symbol_hash.emplace("cqmpt", field);
516 
517   field = new LogField("client_sec_protocol", "cqssv", LogField::STRING, &LogAccess::marshal_client_security_protocol,
518                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
519   global_field_list.add(field, false);
520   field_symbol_hash.emplace("cqssv", field);
521 
522   field = new LogField("client_cipher_suite", "cqssc", LogField::STRING, &LogAccess::marshal_client_security_cipher_suite,
523                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
524   global_field_list.add(field, false);
525   field_symbol_hash.emplace("cqssc", field);
526 
527   field = new LogField("client_curve", "cqssu", LogField::STRING, &LogAccess::marshal_client_security_curve,
528                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
529   global_field_list.add(field, false);
530   field_symbol_hash.emplace("cqssu", field);
531 
532   field = new LogField("client_sec_alpn", "cqssa", LogField::STRING, &LogAccess::marshal_client_security_alpn,
533                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
534   global_field_list.add(field, false);
535   field_symbol_hash.emplace("cqssa", field);
536 
537   Ptr<LogFieldAliasTable> finish_status_map = make_ptr(new LogFieldAliasTable);
538   finish_status_map->init(N_LOG_FINISH_CODE_TYPES, LOG_FINISH_FIN, "FIN", LOG_FINISH_INTR, "INTR", LOG_FINISH_TIMEOUT, "TIMEOUT");
539 
540   field = new LogField("client_finish_status_code", "cfsc", LogField::sINT, &LogAccess::marshal_client_finish_status_code,
541                        &LogAccess::unmarshal_finish_status, make_alias_map(finish_status_map));
542   global_field_list.add(field, false);
543   field_symbol_hash.emplace("cfsc", field);
544 
545   field =
546     new LogField("client_req_id", "crid", LogField::sINT, &LogAccess::marshal_client_req_id, &LogAccess::unmarshal_int_to_str);
547   global_field_list.add(field, false);
548   field_symbol_hash.emplace("crid", field);
549 
550   field = new LogField("client_req_uuid", "cruuid", LogField::STRING, &LogAccess::marshal_client_req_uuid,
551                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
552   global_field_list.add(field, false);
553   field_symbol_hash.emplace("cruuid", field);
554 
555   field = new LogField("client_rx_error_code", "crec", LogField::STRING, &LogAccess::marshal_client_rx_error_code,
556                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
557   global_field_list.add(field, false);
558   field_symbol_hash.emplace("crec", field);
559 
560   field = new LogField("client_tx_error_code", "ctec", LogField::STRING, &LogAccess::marshal_client_tx_error_code,
561                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
562   global_field_list.add(field, false);
563   field_symbol_hash.emplace("ctec", field);
564 
565   field = new LogField("client_request_all_header_fields", "cqah", LogField::STRING,
566                        &LogAccess::marshal_client_req_all_header_fields, &LogUtils::unmarshalMimeHdr);
567   global_field_list.add(field, false);
568   field_symbol_hash.emplace("cqah", field);
569 
570   // proxy -> client fields
571   field = new LogField("proxy_resp_content_type", "psct", LogField::STRING, &LogAccess::marshal_proxy_resp_content_type,
572                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
573   global_field_list.add(field, false);
574   field_symbol_hash.emplace("psct", field);
575 
576   field = new LogField("proxy_resp_reason_phrase", "prrp", LogField::STRING, &LogAccess::marshal_proxy_resp_reason_phrase,
577                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
578   global_field_list.add(field, false);
579   field_symbol_hash.emplace("prrp", field);
580 
581   field = new LogField("proxy_resp_squid_len", "psql", LogField::sINT, &LogAccess::marshal_proxy_resp_squid_len,
582                        &LogAccess::unmarshal_int_to_str);
583   global_field_list.add(field, false);
584   field_symbol_hash.emplace("psql", field);
585 
586   field = new LogField("proxy_resp_content_len", "pscl", LogField::sINT, &LogAccess::marshal_proxy_resp_content_len,
587                        &LogAccess::unmarshal_int_to_str);
588   global_field_list.add(field, false);
589   field_symbol_hash.emplace("pscl", field);
590 
591   field = new LogField("proxy_resp_content_len_hex", "psch", LogField::sINT, &LogAccess::marshal_proxy_resp_content_len,
592                        &LogAccess::unmarshal_int_to_str_hex);
593   global_field_list.add(field, false);
594   field_symbol_hash.emplace("psch", field);
595 
596   field = new LogField("proxy_resp_status_code", "pssc", LogField::sINT, &LogAccess::marshal_proxy_resp_status_code,
597                        &LogAccess::unmarshal_http_status);
598   global_field_list.add(field, false);
599   field_symbol_hash.emplace("pssc", field);
600 
601   field = new LogField("proxy_resp_header_len", "pshl", LogField::sINT, &LogAccess::marshal_proxy_resp_header_len,
602                        &LogAccess::unmarshal_int_to_str);
603   global_field_list.add(field, false);
604   field_symbol_hash.emplace("pshl", field);
605 
606   field = new LogField("proxy_finish_status_code", "pfsc", LogField::sINT, &LogAccess::marshal_proxy_finish_status_code,
607                        &LogAccess::unmarshal_finish_status, make_alias_map(finish_status_map));
608   global_field_list.add(field, false);
609   field_symbol_hash.emplace("pfsc", field);
610 
611   Ptr<LogFieldAliasTable> cache_code_map = make_ptr(new LogFieldAliasTable);
612   cache_code_map->init(
613     52, SQUID_LOG_EMPTY, "UNDEFINED", SQUID_LOG_TCP_HIT, "TCP_HIT", SQUID_LOG_TCP_DISK_HIT, "TCP_DISK_HIT", SQUID_LOG_TCP_MEM_HIT,
614     "TCP_MEM_HIT", SQUID_LOG_TCP_MISS, "TCP_MISS", SQUID_LOG_TCP_EXPIRED_MISS, "TCP_EXPIRED_MISS", SQUID_LOG_TCP_REFRESH_HIT,
615     "TCP_REFRESH_HIT", SQUID_LOG_TCP_REF_FAIL_HIT, "TCP_REFRESH_FAIL_HIT", SQUID_LOG_TCP_REFRESH_MISS, "TCP_REFRESH_MISS",
616     SQUID_LOG_TCP_CLIENT_REFRESH, "TCP_CLIENT_REFRESH_MISS", SQUID_LOG_TCP_IMS_HIT, "TCP_IMS_HIT", SQUID_LOG_TCP_IMS_MISS,
617     "TCP_IMS_MISS", SQUID_LOG_TCP_SWAPFAIL, "TCP_SWAPFAIL_MISS", SQUID_LOG_TCP_DENIED, "TCP_DENIED", SQUID_LOG_TCP_WEBFETCH_MISS,
618     "TCP_WEBFETCH_MISS", SQUID_LOG_TCP_FUTURE_2, "TCP_FUTURE_2", SQUID_LOG_TCP_HIT_REDIRECT, "TCP_HIT_REDIRECT",
619     SQUID_LOG_TCP_MISS_REDIRECT, "TCP_MISS_REDIRECT", SQUID_LOG_TCP_HIT_X_REDIRECT, "TCP_HIT_X_REDIRECT",
620     SQUID_LOG_TCP_MISS_X_REDIRECT, "TCP_MISS_X_REDIRECT", SQUID_LOG_UDP_HIT, "UDP_HIT", SQUID_LOG_UDP_WEAK_HIT, "UDP_WEAK_HIT",
621     SQUID_LOG_UDP_HIT_OBJ, "UDP_HIT_OBJ", SQUID_LOG_UDP_MISS, "UDP_MISS", SQUID_LOG_UDP_DENIED, "UDP_DENIED", SQUID_LOG_UDP_INVALID,
622     "UDP_INVALID", SQUID_LOG_UDP_RELOADING, "UDP_RELOADING", SQUID_LOG_UDP_FUTURE_1, "UDP_FUTURE_1", SQUID_LOG_UDP_FUTURE_2,
623     "UDP_FUTURE_2", SQUID_LOG_ERR_READ_TIMEOUT, "ERR_READ_TIMEOUT", SQUID_LOG_ERR_LIFETIME_EXP, "ERR_LIFETIME_EXP",
624     SQUID_LOG_ERR_POST_ENTITY_TOO_LARGE, "ERR_POST_ENTITY_TOO_LARGE", SQUID_LOG_ERR_NO_CLIENTS_BIG_OBJ, "ERR_NO_CLIENTS_BIG_OBJ",
625     SQUID_LOG_ERR_READ_ERROR, "ERR_READ_ERROR", SQUID_LOG_ERR_CLIENT_ABORT, "ERR_CLIENT_ABORT", SQUID_LOG_ERR_CLIENT_READ_ERROR,
626     "ERR_CLIENT_READ_ERROR", SQUID_LOG_ERR_CONNECT_FAIL, "ERR_CONNECT_FAIL", SQUID_LOG_ERR_INVALID_REQ, "ERR_INVALID_REQ",
627     SQUID_LOG_ERR_UNSUP_REQ, "ERR_UNSUP_REQ", SQUID_LOG_ERR_INVALID_URL, "ERR_INVALID_URL", SQUID_LOG_ERR_NO_FDS, "ERR_NO_FDS",
628     SQUID_LOG_ERR_DNS_FAIL, "ERR_DNS_FAIL", SQUID_LOG_ERR_NOT_IMPLEMENTED, "ERR_NOT_IMPLEMENTED", SQUID_LOG_ERR_CANNOT_FETCH,
629     "ERR_CANNOT_FETCH", SQUID_LOG_ERR_NO_RELAY, "ERR_NO_RELAY", SQUID_LOG_ERR_DISK_IO, "ERR_DISK_IO",
630     SQUID_LOG_ERR_ZERO_SIZE_OBJECT, "ERR_ZERO_SIZE_OBJECT", SQUID_LOG_ERR_PROXY_DENIED, "ERR_PROXY_DENIED",
631     SQUID_LOG_ERR_WEBFETCH_DETECTED, "ERR_WEBFETCH_DETECTED", SQUID_LOG_ERR_FUTURE_1, "ERR_FUTURE_1", SQUID_LOG_ERR_LOOP_DETECTED,
632     "ERR_LOOP_DETECTED", SQUID_LOG_ERR_UNKNOWN, "ERR_UNKNOWN");
633 
634   Ptr<LogFieldAliasTable> cache_subcode_map = make_ptr(new LogFieldAliasTable);
635   cache_subcode_map->init(2, SQUID_SUBCODE_EMPTY, "NONE", SQUID_SUBCODE_NUM_REDIRECTIONS_EXCEEDED, "NUM_REDIRECTIONS_EXCEEDED");
636 
637   Ptr<LogFieldAliasTable> cache_hit_miss_map = make_ptr(new LogFieldAliasTable);
638   cache_hit_miss_map->init(21, SQUID_HIT_RESERVED, "HIT", SQUID_HIT_LEVEL_1, "HIT_RAM", // Also SQUID_HIT_RAM
639                            SQUID_HIT_LEVEL_2, "HIT_SSD",                                // Also SQUID_HIT_SSD
640                            SQUID_HIT_LEVEL_3, "HIT_DISK",                               // Also SQUID_HIT_DISK
641                            SQUID_HIT_LEVEL_4, "HIT_CLUSTER",                            // Also SQUID_HIT_CLUSTER
642                            SQUID_HIT_LEVEL_5, "HIT_NET",                                // Also SQUID_HIT_NET
643                            SQUID_HIT_LEVEL_6, "HIT_LEVEL_6", SQUID_HIT_LEVEL_7, "HIT_LEVEL_7", SQUID_HIT_LEVEL_8, "HIT_LEVEL_8",
644                            SQUID_HIT_LEVEl_9, "HIT_LEVEL_9", SQUID_MISS_NONE, "MISS", SQUID_MISS_HTTP_NON_CACHE,
645                            "MISS_HTTP_NON_CACHE", SQUID_MISS_HTTP_NO_DLE, "MISS_HTTP_NO_DLE", SQUID_MISS_HTTP_NO_LE,
646                            "MISS_HTTP_NO_LE", SQUID_MISS_HTTP_CONTENT, "MISS_HTTP_CONTENT", SQUID_MISS_PRAGMA_NOCACHE,
647                            "MISS_PRAGMA_NOCACHE", SQUID_MISS_PASS, "MISS_PASS", SQUID_MISS_PRE_EXPIRED, "MISS_PRE_EXPIRED",
648                            SQUID_MISS_ERROR, "MISS_ERROR", SQUID_MISS_CACHE_BYPASS, "MISS_CACHE_BYPASS",
649                            SQUID_HIT_MISS_INVALID_ASSIGNED_CODE, "INVALID_CODE");
650 
651   field = new LogField("cache_result_code", "crc", LogField::sINT, &LogAccess::marshal_cache_result_code,
652                        &LogAccess::unmarshal_cache_code, make_alias_map(cache_code_map));
653   global_field_list.add(field, false);
654   field_symbol_hash.emplace("crc", field);
655 
656   // Reuse the unmarshalling code from crc
657   field = new LogField("cache_result_subcode", "crsc", LogField::sINT, &LogAccess::marshal_cache_result_subcode,
658                        &LogAccess::unmarshal_cache_code, make_alias_map(cache_subcode_map));
659   global_field_list.add(field, false);
660   field_symbol_hash.emplace("crsc", field);
661 
662   field = new LogField("cache_hit_miss", "chm", LogField::sINT, &LogAccess::marshal_cache_hit_miss,
663                        &LogAccess::unmarshal_cache_hit_miss, make_alias_map(cache_hit_miss_map));
664   global_field_list.add(field, false);
665   field_symbol_hash.emplace("chm", field);
666 
667   field = new LogField("proxy_response_all_header_fields", "psah", LogField::STRING,
668                        &LogAccess::marshal_proxy_resp_all_header_fields, &LogUtils::unmarshalMimeHdr);
669   global_field_list.add(field, false);
670   field_symbol_hash.emplace("psah", field);
671 
672   // proxy -> server fields
673   field = new LogField("proxy_req_header_len", "pqhl", LogField::sINT, &LogAccess::marshal_proxy_req_header_len,
674                        &LogAccess::unmarshal_int_to_str);
675   global_field_list.add(field, false);
676   field_symbol_hash.emplace("pqhl", field);
677 
678   field = new LogField("proxy_req_squid_len", "pqql", LogField::sINT, &LogAccess::marshal_proxy_req_squid_len,
679                        &LogAccess::unmarshal_int_to_str);
680   global_field_list.add(field, false);
681   field_symbol_hash.emplace("pqql", field);
682 
683   field = new LogField("proxy_req_content_len", "pqcl", LogField::sINT, &LogAccess::marshal_proxy_req_content_len,
684                        &LogAccess::unmarshal_int_to_str);
685   global_field_list.add(field, false);
686   field_symbol_hash.emplace("pqcl", field);
687 
688   field = new LogField("proxy_req_server_ip", "pqsi", LogField::IP, &LogAccess::marshal_proxy_req_server_ip,
689                        &LogAccess::unmarshal_ip_to_str);
690   global_field_list.add(field, false);
691   field_symbol_hash.emplace("pqsi", field);
692 
693   field = new LogField("proxy_req_server_port", "pqsp", LogField::sINT, &LogAccess::marshal_proxy_req_server_port,
694                        &LogAccess::unmarshal_int_to_str);
695   global_field_list.add(field, false);
696   field_symbol_hash.emplace("pqsp", field);
697 
698   field = new LogField("next_hop_ip", "nhi", LogField::IP, &LogAccess::marshal_next_hop_ip, &LogAccess::unmarshal_ip_to_str);
699   global_field_list.add(field, false);
700   field_symbol_hash.emplace("nhi", field);
701 
702   field = new LogField("next_hop_port", "nhp", LogField::IP, &LogAccess::marshal_next_hop_port, &LogAccess::unmarshal_int_to_str);
703   global_field_list.add(field, false);
704   field_symbol_hash.emplace("nhp", field);
705 
706   Ptr<LogFieldAliasTable> hierarchy_map = make_ptr(new LogFieldAliasTable);
707   hierarchy_map->init(
708     36, SQUID_HIER_EMPTY, "EMPTY", SQUID_HIER_NONE, "NONE", SQUID_HIER_DIRECT, "DIRECT", SQUID_HIER_SIBLING_HIT, "SIBLING_HIT",
709     SQUID_HIER_PARENT_HIT, "PARENT_HIT", SQUID_HIER_DEFAULT_PARENT, "DEFAULT_PARENT", SQUID_HIER_SINGLE_PARENT, "SINGLE_PARENT",
710     SQUID_HIER_FIRST_UP_PARENT, "FIRST_UP_PARENT", SQUID_HIER_NO_PARENT_DIRECT, "NO_PARENT_DIRECT", SQUID_HIER_FIRST_PARENT_MISS,
711     "FIRST_PARENT_MISS", SQUID_HIER_LOCAL_IP_DIRECT, "LOCAL_IP_DIRECT", SQUID_HIER_FIREWALL_IP_DIRECT, "FIREWALL_IP_DIRECT",
712     SQUID_HIER_NO_DIRECT_FAIL, "NO_DIRECT_FAIL", SQUID_HIER_SOURCE_FASTEST, "SOURCE_FASTEST", SQUID_HIER_SIBLING_UDP_HIT_OBJ,
713     "SIBLING_UDP_HIT_OBJ", SQUID_HIER_PARENT_UDP_HIT_OBJ, "PARENT_UDP_HIT_OBJ", SQUID_HIER_PASSTHROUGH_PARENT, "PASSTHROUGH_PARENT",
714     SQUID_HIER_SSL_PARENT_MISS, "SSL_PARENT_MISS", SQUID_HIER_INVALID_CODE, "INVALID_CODE", SQUID_HIER_TIMEOUT_DIRECT,
715     "TIMEOUT_DIRECT", SQUID_HIER_TIMEOUT_SIBLING_HIT, "TIMEOUT_SIBLING_HIT", SQUID_HIER_TIMEOUT_PARENT_HIT, "TIMEOUT_PARENT_HIT",
716     SQUID_HIER_TIMEOUT_DEFAULT_PARENT, "TIMEOUT_DEFAULT_PARENT", SQUID_HIER_TIMEOUT_SINGLE_PARENT, "TIMEOUT_SINGLE_PARENT",
717     SQUID_HIER_TIMEOUT_FIRST_UP_PARENT, "TIMEOUT_FIRST_UP_PARENT", SQUID_HIER_TIMEOUT_NO_PARENT_DIRECT, "TIMEOUT_NO_PARENT_DIRECT",
718     SQUID_HIER_TIMEOUT_FIRST_PARENT_MISS, "TIMEOUT_FIRST_PARENT_MISS", SQUID_HIER_TIMEOUT_LOCAL_IP_DIRECT,
719     "TIMEOUT_LOCAL_IP_DIRECT", SQUID_HIER_TIMEOUT_FIREWALL_IP_DIRECT, "TIMEOUT_FIREWALL_IP_DIRECT",
720     SQUID_HIER_TIMEOUT_NO_DIRECT_FAIL, "TIMEOUT_NO_DIRECT_FAIL", SQUID_HIER_TIMEOUT_SOURCE_FASTEST, "TIMEOUT_SOURCE_FASTEST",
721     SQUID_HIER_TIMEOUT_SIBLING_UDP_HIT_OBJ, "TIMEOUT_SIBLING_UDP_HIT_OBJ", SQUID_HIER_TIMEOUT_PARENT_UDP_HIT_OBJ,
722     "TIMEOUT_PARENT_UDP_HIT_OBJ", SQUID_HIER_TIMEOUT_PASSTHROUGH_PARENT, "TIMEOUT_PASSTHROUGH_PARENT",
723     SQUID_HIER_TIMEOUT_TIMEOUT_SSL_PARENT_MISS, "TIMEOUT_TIMEOUT_SSL_PARENT_MISS", SQUID_HIER_INVALID_ASSIGNED_CODE,
724     "INVALID_ASSIGNED_CODE");
725 
726   field = new LogField("proxy_hierarchy_route", "phr", LogField::sINT, &LogAccess::marshal_proxy_hierarchy_route,
727                        &LogAccess::unmarshal_hierarchy, make_alias_map(hierarchy_map));
728   global_field_list.add(field, false);
729   field_symbol_hash.emplace("phr", field);
730 
731   field = new LogField("proxy_host_name", "phn", LogField::STRING, &LogAccess::marshal_proxy_host_name,
732                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
733   global_field_list.add(field, false);
734   field_symbol_hash.emplace("phn", field);
735 
736   field = new LogField("proxy_host_ip", "phi", LogField::IP, &LogAccess::marshal_proxy_host_ip, &LogAccess::unmarshal_ip_to_str);
737   global_field_list.add(field, false);
738   field_symbol_hash.emplace("phi", field);
739 
740   field =
741     new LogField("proxy_host_port", "php", LogField::sINT, &LogAccess::marshal_proxy_host_port, &LogAccess::unmarshal_int_to_str);
742   global_field_list.add(field, false);
743   field_symbol_hash.emplace("php", field);
744 
745   field = new LogField("proxy_req_is_ssl", "pqssl", LogField::sINT, &LogAccess::marshal_proxy_req_is_ssl,
746                        &LogAccess::unmarshal_int_to_str);
747   global_field_list.add(field, false);
748   field_symbol_hash.emplace("pqssl", field);
749 
750   field = new LogField("proxy_request_all_header_fields", "pqah", LogField::STRING, &LogAccess::marshal_proxy_req_all_header_fields,
751                        &LogUtils::unmarshalMimeHdr);
752   global_field_list.add(field, false);
753   field_symbol_hash.emplace("pqah", field);
754 
755   // server -> proxy fields
756   field = new LogField("server_host_ip", "shi", LogField::IP, &LogAccess::marshal_server_host_ip, &LogAccess::unmarshal_ip_to_str);
757 
758   global_field_list.add(field, false);
759   field_symbol_hash.emplace("shi", field);
760 
761   field = new LogField("server_host_name", "shn", LogField::STRING, &LogAccess::marshal_server_host_name,
762                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
763   global_field_list.add(field, false);
764   field_symbol_hash.emplace("shn", field);
765 
766   field = new LogField("server_resp_status_code", "sssc", LogField::sINT, &LogAccess::marshal_server_resp_status_code,
767                        &LogAccess::unmarshal_http_status);
768   global_field_list.add(field, false);
769   field_symbol_hash.emplace("sssc", field);
770 
771   field = new LogField("server_resp_content_len", "sscl", LogField::sINT, &LogAccess::marshal_server_resp_content_len,
772                        &LogAccess::unmarshal_int_to_str);
773   global_field_list.add(field, false);
774   field_symbol_hash.emplace("sscl", field);
775 
776   field = new LogField("server_resp_header_len", "sshl", LogField::sINT, &LogAccess::marshal_server_resp_header_len,
777                        &LogAccess::unmarshal_int_to_str);
778   global_field_list.add(field, false);
779   field_symbol_hash.emplace("sshl", field);
780 
781   field = new LogField("server_resp_squid_len", "ssql", LogField::sINT, &LogAccess::marshal_server_resp_squid_len,
782                        &LogAccess::unmarshal_int_to_str);
783   global_field_list.add(field, false);
784   field_symbol_hash.emplace("ssql", field);
785 
786   field = new LogField("server_resp_http_version", "sshv", LogField::dINT, &LogAccess::marshal_server_resp_http_version,
787                        &LogAccess::unmarshal_http_version);
788   global_field_list.add(field, false);
789   field_symbol_hash.emplace("sshv", field);
790 
791   field = new LogField("server_resp_time", "stms", LogField::sINT, &LogAccess::marshal_server_resp_time_ms,
792                        &LogAccess::unmarshal_int_to_str);
793   global_field_list.add(field, false);
794   field_symbol_hash.emplace("stms", field);
795 
796   field = new LogField("server_resp_time_hex", "stmsh", LogField::sINT, &LogAccess::marshal_server_resp_time_ms,
797                        &LogAccess::unmarshal_int_to_str_hex);
798   global_field_list.add(field, false);
799   field_symbol_hash.emplace("stmsh", field);
800 
801   field = new LogField("server_resp_time_fractional", "stmsf", LogField::sINT, &LogAccess::marshal_server_resp_time_ms,
802                        &LogAccess::unmarshal_ttmsf);
803   global_field_list.add(field, false);
804   field_symbol_hash.emplace("stmsf", field);
805 
806   field = new LogField("server_resp_time_sec", "sts", LogField::sINT, &LogAccess::marshal_server_resp_time_s,
807                        &LogAccess::unmarshal_int_to_str);
808   global_field_list.add(field, false);
809   field_symbol_hash.emplace("sts", field);
810 
811   field = new LogField("server_transact_count", "sstc", LogField::sINT, &LogAccess::marshal_server_transact_count,
812                        &LogAccess::unmarshal_int_to_str);
813   global_field_list.add(field, false);
814   field_symbol_hash.emplace("sstc", field);
815 
816   field = new LogField("server_connect_attempts", "sca", LogField::sINT, &LogAccess::marshal_server_connect_attempts,
817                        &LogAccess::unmarshal_int_to_str);
818   global_field_list.add(field, false);
819   field_symbol_hash.emplace("sca", field);
820 
821   field = new LogField("origin_response_all_header_fields", "ssah", LogField::STRING,
822                        &LogAccess::marshal_server_resp_all_header_fields, &LogUtils::unmarshalMimeHdr);
823   global_field_list.add(field, false);
824   field_symbol_hash.emplace("ssah", field);
825 
826   field = new LogField("cached_resp_status_code", "csssc", LogField::sINT, &LogAccess::marshal_cache_resp_status_code,
827                        &LogAccess::unmarshal_http_status);
828   global_field_list.add(field, false);
829   field_symbol_hash.emplace("csssc", field);
830 
831   field = new LogField("cached_resp_content_len", "csscl", LogField::sINT, &LogAccess::marshal_cache_resp_content_len,
832                        &LogAccess::unmarshal_int_to_str);
833   global_field_list.add(field, false);
834   field_symbol_hash.emplace("csscl", field);
835 
836   field = new LogField("cached_resp_header_len", "csshl", LogField::sINT, &LogAccess::marshal_cache_resp_header_len,
837                        &LogAccess::unmarshal_int_to_str);
838   global_field_list.add(field, false);
839   field_symbol_hash.emplace("csshl", field);
840 
841   field = new LogField("cached_resp_squid_len", "cssql", LogField::sINT, &LogAccess::marshal_cache_resp_squid_len,
842                        &LogAccess::unmarshal_int_to_str);
843   global_field_list.add(field, false);
844   field_symbol_hash.emplace("cssql", field);
845 
846   field = new LogField("cached_resp_http_version", "csshv", LogField::dINT, &LogAccess::marshal_cache_resp_http_version,
847                        &LogAccess::unmarshal_http_version);
848   global_field_list.add(field, false);
849   field_symbol_hash.emplace("csshv", field);
850 
851   field = new LogField("cache_origin_response_all_header_fields", "cssah", LogField::STRING,
852                        &LogAccess::marshal_cache_resp_all_header_fields, &LogUtils::unmarshalMimeHdr);
853   global_field_list.add(field, false);
854   field_symbol_hash.emplace("cssah", field);
855 
856   field = new LogField("client_retry_after_time", "crat", LogField::sINT, &LogAccess::marshal_client_retry_after_time,
857                        &LogAccess::unmarshal_int_to_str);
858   global_field_list.add(field, false);
859   field_symbol_hash.emplace("crat", field);
860 
861   // cache write fields
862 
863   Ptr<LogFieldAliasTable> cache_write_code_map = make_ptr(new LogFieldAliasTable);
864   cache_write_code_map->init(N_LOG_CACHE_WRITE_TYPES, LOG_CACHE_WRITE_NONE, "-", LOG_CACHE_WRITE_LOCK_MISSED, "WL_MISS",
865                              LOG_CACHE_WRITE_LOCK_ABORTED, "INTR", LOG_CACHE_WRITE_ERROR, "ERR", LOG_CACHE_WRITE_COMPLETE, "FIN");
866   field = new LogField("cache_write_result", "cwr", LogField::sINT, &LogAccess::marshal_cache_write_code,
867                        &LogAccess::unmarshal_cache_write_code, make_alias_map(cache_write_code_map));
868   global_field_list.add(field, false);
869   field_symbol_hash.emplace("cwr", field);
870 
871   field = new LogField("cache_write_transform_result", "cwtr", LogField::sINT, &LogAccess::marshal_cache_write_transform_code,
872                        &LogAccess::unmarshal_cache_write_code, make_alias_map(cache_write_code_map));
873   global_field_list.add(field, false);
874   field_symbol_hash.emplace("cwtr", field);
875 
876   // other fields
877 
878   field = new LogField("transfer_time_ms", "ttms", LogField::sINT, &LogAccess::marshal_transfer_time_ms,
879                        &LogAccess::unmarshal_int_to_str);
880   global_field_list.add(field, false);
881   field_symbol_hash.emplace("ttms", field);
882 
883   field = new LogField("transfer_time_ms_hex", "ttmh", LogField::sINT, &LogAccess::marshal_transfer_time_ms,
884                        &LogAccess::unmarshal_int_to_str_hex);
885   global_field_list.add(field, false);
886   field_symbol_hash.emplace("ttmh", field);
887 
888   field = new LogField("transfer_time_ms_fractional", "ttmsf", LogField::sINT, &LogAccess::marshal_transfer_time_ms,
889                        &LogAccess::unmarshal_ttmsf);
890   global_field_list.add(field, false);
891   field_symbol_hash.emplace("ttmsf", field);
892 
893   field =
894     new LogField("transfer_time_sec", "tts", LogField::sINT, &LogAccess::marshal_transfer_time_s, &LogAccess::unmarshal_int_to_str);
895   global_field_list.add(field, false);
896   field_symbol_hash.emplace("tts", field);
897 
898   field = new LogField("file_size", "fsiz", LogField::sINT, &LogAccess::marshal_file_size, &LogAccess::unmarshal_int_to_str);
899   global_field_list.add(field, false);
900   field_symbol_hash.emplace("fsiz", field);
901 
902   field = new LogField("client_connection_id", "ccid", LogField::sINT, &LogAccess::marshal_client_http_connection_id,
903                        &LogAccess::unmarshal_int_to_str);
904   global_field_list.add(field, false);
905   field_symbol_hash.emplace("ccid", field);
906 
907   field = new LogField("client_transaction_id", "ctid", LogField::sINT, &LogAccess::marshal_client_http_transaction_id,
908                        &LogAccess::unmarshal_int_to_str);
909   global_field_list.add(field, false);
910   field_symbol_hash.emplace("ctid", field);
911 
912   field = new LogField("cache_read_retry_attempts", "crra", LogField::dINT, &LogAccess::marshal_cache_read_retries,
913                        &LogAccess::unmarshal_int_to_str);
914   global_field_list.add(field, false);
915   field_symbol_hash.emplace("crra", field);
916 
917   field = new LogField("cache_write_retry_attempts", "cwra", LogField::dINT, &LogAccess::marshal_cache_write_retries,
918                        &LogAccess::unmarshal_int_to_str);
919   global_field_list.add(field, false);
920   field_symbol_hash.emplace("cwra", field);
921 
922   field = new LogField("cache_collapsed_connection_success", "cccs", LogField::dINT,
923                        &LogAccess::marshal_cache_collapsed_connection_success, &LogAccess::unmarshal_int_to_str);
924   global_field_list.add(field, false);
925   field_symbol_hash.emplace("cccs", field);
926 
927   field = new LogField("client_transaction_priority_weight", "ctpw", LogField::sINT,
928                        &LogAccess::marshal_client_http_transaction_priority_weight, &LogAccess::unmarshal_int_to_str);
929   global_field_list.add(field, false);
930   field_symbol_hash.emplace("ctpw", field);
931 
932   field = new LogField("client_transaction_priority_dependence", "ctpd", LogField::sINT,
933                        &LogAccess::marshal_client_http_transaction_priority_dependence, &LogAccess::unmarshal_int_to_str);
934   global_field_list.add(field, false);
935   field_symbol_hash.emplace("ctpd", field);
936 
937   field = new LogField("proxy_protocol_version", "ppv", LogField::dINT, &LogAccess::marshal_proxy_protocol_version,
938                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
939   global_field_list.add(field, false);
940   field_symbol_hash.emplace("ppv", field);
941 
942   field = new LogField("proxy_protocol_src_ip", "pps", LogField::IP, &LogAccess::marshal_proxy_protocol_src_ip,
943                        &LogAccess::unmarshal_ip_to_str);
944   global_field_list.add(field, false);
945   field_symbol_hash.emplace("ppsip", field);
946 
947   field = new LogField("proxy_protocol_dst_ip", "ppd", LogField::IP, &LogAccess::marshal_proxy_protocol_dst_ip,
948                        &LogAccess::unmarshal_ip_to_str);
949   global_field_list.add(field, false);
950   field_symbol_hash.emplace("ppdip", field);
951 
952   field = new LogField("version_build_number", "vbn", LogField::STRING, &LogAccess::marshal_version_build_number,
953                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
954   global_field_list.add(field, false);
955   field_symbol_hash.emplace("vbn", field);
956 
957   field = new LogField("version_string", "vs", LogField::STRING, &LogAccess::marshal_version_string,
958                        reinterpret_cast<LogField::UnmarshalFunc>(&LogAccess::unmarshal_str));
959   global_field_list.add(field, false);
960   field_symbol_hash.emplace("vs", field);
961 
962   init_status |= FIELDS_INITIALIZED;
963 }
964 
965 /*-------------------------------------------------------------------------
966 
967   Initialization functions
968 
969   -------------------------------------------------------------------------*/
970 int
handle_logging_mode_change(const char *,RecDataT,RecData,void *)971 Log::handle_logging_mode_change(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */,
972                                 RecData /* data ATS_UNUSED */, void * /* cookie ATS_UNUSED */)
973 {
974   Debug("log-config", "Enabled status changed");
975   logging_mode_changed = true;
976   return 0;
977 }
978 
979 int
handle_periodic_tasks_int_change(const char *,RecDataT,RecData data,void *)980 Log::handle_periodic_tasks_int_change(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */, RecData data,
981                                       void * /* cookie ATS_UNSED */)
982 {
983   Debug("log-periodic", "periodic task interval changed");
984   if (data.rec_int <= 0) {
985     periodic_tasks_interval = PERIODIC_TASKS_INTERVAL_FALLBACK;
986     Error("new periodic tasks interval = %d is invalid, falling back to default = %d", (int)data.rec_int,
987           PERIODIC_TASKS_INTERVAL_FALLBACK);
988   } else {
989     periodic_tasks_interval = static_cast<uint32_t>(data.rec_int);
990     Debug("log-periodic", "periodic task interval changed to %u", periodic_tasks_interval);
991   }
992   return REC_ERR_OKAY;
993 }
994 
995 int
handle_log_rotation_request()996 Log::handle_log_rotation_request()
997 {
998   Debug("log", "Request to reopen rotated log files.");
999   log_rotate_signal_received = true;
1000   return 0;
1001 }
1002 
1003 void
init(int flags)1004 Log::init(int flags)
1005 {
1006   preproc_threads = 1;
1007 
1008   // store the configuration flags
1009   //
1010   config_flags = flags;
1011 
1012   // create the configuration object
1013   config = new LogConfig();
1014   ink_assert(config != nullptr);
1015 
1016   log_configid = configProcessor.set(log_configid, config);
1017 
1018   // set the logging_mode and read config variables if needed
1019   //
1020   if (config_flags & LOGCAT) {
1021     logging_mode = LOG_MODE_NONE;
1022   } else {
1023     log_rsb = RecAllocateRawStatBlock(static_cast<int>(log_stat_count));
1024     LogConfig::register_stat_callbacks();
1025 
1026     config->read_configuration_variables();
1027     preproc_threads = config->preproc_threads;
1028 
1029     int val = static_cast<int>(REC_ConfigReadInteger("proxy.config.log.logging_enabled"));
1030     if (val < LOG_MODE_NONE || val > LOG_MODE_FULL) {
1031       logging_mode = LOG_MODE_FULL;
1032       Warning("proxy.config.log.logging_enabled has an invalid "
1033               "value, setting it to %d",
1034               logging_mode);
1035     } else {
1036       logging_mode = static_cast<LoggingMode>(val);
1037     }
1038     // periodic task interval are set on a per instance basis
1039     MgmtInt pti = REC_ConfigReadInteger("proxy.config.log.periodic_tasks_interval");
1040     if (pti <= 0) {
1041       Error("proxy.config.log.periodic_tasks_interval = %" PRId64 " is invalid", pti);
1042       Note("falling back to default periodic tasks interval = %d", PERIODIC_TASKS_INTERVAL_FALLBACK);
1043       periodic_tasks_interval = PERIODIC_TASKS_INTERVAL_FALLBACK;
1044     } else {
1045       periodic_tasks_interval = static_cast<uint32_t>(pti);
1046     }
1047 
1048     REC_RegisterConfigUpdateFunc("proxy.config.log.periodic_tasks_interval", &Log::handle_periodic_tasks_int_change, nullptr);
1049   }
1050 
1051   // if remote management is enabled, do all necessary initialization to
1052   // be able to handle a logging mode change
1053   //
1054   if (!(config_flags & NO_REMOTE_MANAGEMENT)) {
1055     REC_RegisterConfigUpdateFunc("proxy.config.log.logging_enabled", &Log::handle_logging_mode_change, nullptr);
1056 
1057     // Clear any stat values that need to be reset on startup
1058     //
1059     RecSetRawStatSum(log_rsb, log_stat_log_files_open_stat, 0);
1060     RecSetRawStatCount(log_rsb, log_stat_log_files_open_stat, 0);
1061   }
1062 
1063   init_fields();
1064   if (!(config_flags & LOGCAT)) {
1065     Debug("log-config", "Log::init(): logging_mode = %d init status = %d", logging_mode, init_status);
1066     config->init();
1067     init_when_enabled();
1068   }
1069 }
1070 
1071 void
init_when_enabled()1072 Log::init_when_enabled()
1073 {
1074   // make sure the log config has been initialized
1075   ink_release_assert(config->initialized == true);
1076 
1077   if (!(init_status & FULLY_INITIALIZED)) {
1078     // register callbacks
1079     //
1080     if (!(config_flags & NO_REMOTE_MANAGEMENT)) {
1081       LogConfig::register_config_callbacks();
1082     }
1083 
1084     LogConfig::register_mgmt_callbacks();
1085 
1086     // create the flush thread
1087     create_threads();
1088     eventProcessor.schedule_every(new PeriodicWakeup(preproc_threads, 1), HRTIME_SECOND, ET_CALL);
1089 
1090     init_status |= FULLY_INITIALIZED;
1091   }
1092 
1093   Note("logging initialized[%d], logging_mode = %d", init_status, logging_mode);
1094   if (is_debug_tag_set("log-config")) {
1095     config->display();
1096   }
1097 }
1098 
1099 void
create_threads()1100 Log::create_threads()
1101 {
1102   char desc[64];
1103   preproc_notify = new EventNotify[preproc_threads];
1104 
1105   size_t stacksize;
1106   REC_ReadConfigInteger(stacksize, "proxy.config.thread.default.stacksize");
1107 
1108   // start the preproc threads
1109   //
1110   // no need for the conditional var since it will be relying on
1111   // on the event system.
1112   for (int i = 0; i < preproc_threads; i++) {
1113     Continuation *preproc_cont = new LoggingPreprocContinuation(i);
1114     sprintf(desc, "[LOG_PREPROC %d]", i);
1115     eventProcessor.spawn_thread(preproc_cont, desc, stacksize);
1116   }
1117 
1118   // Now, only one flush thread is supported.
1119   // TODO: Enable multiple flush threads, such as
1120   //       one flush thread per file.
1121   //
1122   flush_notify    = new EventNotify;
1123   flush_data_list = new InkAtomicList;
1124 
1125   ink_atomiclist_init(flush_data_list, "Logging flush buffer list", 0);
1126   Continuation *flush_cont = new LoggingFlushContinuation(0);
1127   eventProcessor.spawn_thread(flush_cont, "[LOG_FLUSH]", stacksize);
1128 }
1129 
1130 /*-------------------------------------------------------------------------
1131   Log::access
1132 
1133   Make an entry in the access log for the data supplied by the given
1134   LogAccess object.
1135   -------------------------------------------------------------------------*/
1136 
1137 int
access(LogAccess * lad)1138 Log::access(LogAccess *lad)
1139 {
1140   // See if transaction logging is disabled
1141   //
1142   if (!transaction_logging_enabled()) {
1143     return Log::SKIP;
1144   }
1145 
1146   ink_assert(init_status & FULLY_INITIALIZED);
1147   ink_assert(lad != nullptr);
1148 
1149   int ret;
1150   static long sample = 1;
1151   long this_sample;
1152   ProxyMutex *mutex = this_ethread()->mutex.get();
1153 
1154   // See if we're sampling and it is not time for another sample
1155   //
1156   if (Log::config->sampling_frequency > 1) {
1157     this_sample = sample++;
1158     if (this_sample && this_sample % Log::config->sampling_frequency) {
1159       Debug("log", "sampling, skipping this entry ...");
1160       RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_access_skip_stat, 1);
1161       ret = Log::SKIP;
1162       goto done;
1163     } else {
1164       Debug("log", "sampling, LOGGING this entry ...");
1165       sample = 1;
1166     }
1167   }
1168 
1169   if (Log::config->log_object_manager.get_num_objects() == 0) {
1170     Debug("log", "no log objects, skipping this entry ...");
1171     RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_access_skip_stat, 1);
1172     ret = Log::SKIP;
1173     goto done;
1174   }
1175   // initialize this LogAccess object and process
1176   //
1177   lad->init();
1178   ret = config->log_object_manager.log(lad);
1179 
1180 done:
1181   return ret;
1182 }
1183 
1184 /*-------------------------------------------------------------------------
1185   Log::error
1186 
1187   Make an entry into the current error log.  For convenience, it is given in
1188   both variable argument (format, ...) and stdarg (format, va_list) forms.
1189   -------------------------------------------------------------------------*/
1190 
1191 int
error(const char * format,...)1192 Log::error(const char *format, ...)
1193 {
1194   va_list ap;
1195   int ret;
1196 
1197   va_start(ap, format);
1198   ret = Log::va_error(format, ap);
1199   va_end(ap);
1200 
1201   return ret;
1202 }
1203 
1204 int
va_error(const char * format,va_list ap)1205 Log::va_error(const char *format, va_list ap)
1206 {
1207   int ret_val       = Log::SKIP;
1208   ProxyMutex *mutex = this_ethread()->mutex.get();
1209 
1210   if (error_log) {
1211     ink_assert(format != nullptr);
1212     ret_val = error_log->va_log(nullptr, format, ap);
1213 
1214     switch (ret_val) {
1215     case Log::LOG_OK:
1216       RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_error_ok_stat, 1);
1217       break;
1218     case Log::SKIP:
1219       RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_error_skip_stat, 1);
1220       break;
1221     case Log::AGGR:
1222       RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_error_aggr_stat, 1);
1223       break;
1224     case Log::FULL:
1225       RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_error_full_stat, 1);
1226       break;
1227     case Log::FAIL:
1228       RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_error_fail_stat, 1);
1229       break;
1230     default:
1231       ink_release_assert(!"Unexpected result");
1232     }
1233 
1234     return ret_val;
1235   }
1236 
1237   RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_event_log_error_skip_stat, 1);
1238 
1239   return ret_val;
1240 }
1241 
1242 /*-------------------------------------------------------------------------
1243   Log::trace
1244 
1245   These functions are used for wiretracing of incoming SSL connections.
1246   They are an extension of the existing Log::error functionality but with
1247   special formatting and handling of the non null terminated buffer.
1248   -------------------------------------------------------------------------*/
1249 
1250 void
trace_in(const sockaddr * peer_addr,uint16_t peer_port,const char * format_string,...)1251 Log::trace_in(const sockaddr *peer_addr, uint16_t peer_port, const char *format_string, ...)
1252 {
1253   va_list ap;
1254   va_start(ap, format_string);
1255   trace_va(true, peer_addr, peer_port, format_string, ap);
1256   va_end(ap);
1257 }
1258 
1259 void
trace_out(const sockaddr * peer_addr,uint16_t peer_port,const char * format_string,...)1260 Log::trace_out(const sockaddr *peer_addr, uint16_t peer_port, const char *format_string, ...)
1261 {
1262   va_list ap;
1263   va_start(ap, format_string);
1264   trace_va(false, peer_addr, peer_port, format_string, ap);
1265   va_end(ap);
1266 }
1267 
1268 void
trace_va(bool in,const sockaddr * peer_addr,uint16_t peer_port,const char * format_string,va_list ap)1269 Log::trace_va(bool in, const sockaddr *peer_addr, uint16_t peer_port, const char *format_string, va_list ap)
1270 {
1271   if (!peer_addr || !format_string) {
1272     return;
1273   }
1274 
1275   char ip[INET6_ADDRSTRLEN];
1276   ats_ip_ntop(peer_addr, ip, sizeof(ip));
1277 
1278   struct timeval tp = ink_gettimeofday();
1279 
1280   Log::error("[%9d.%03d] Trace {0x%" PRIx64 "} %s %s:%d: ", static_cast<int>(tp.tv_sec), static_cast<int>(tp.tv_usec / 1000),
1281              reinterpret_cast<uint64_t>(ink_thread_self()), in ? "RECV" : "SEND", ip, peer_port);
1282   Log::va_error(format_string, ap);
1283   Log::error("[End Trace]\n");
1284 }
1285 
1286 /*-------------------------------------------------------------------------
1287   Log::preproc_thread_main
1288 
1289   This function defines the functionality of the logging flush preprocess
1290   thread, whose purpose is to consume full LogBuffer objects, do some prepare
1291   work (such as convert to ascii), and then forward to flush thread.
1292   -------------------------------------------------------------------------*/
1293 
1294 void *
preproc_thread_main(void * args)1295 Log::preproc_thread_main(void *args)
1296 {
1297   int idx = *static_cast<int *>(args);
1298 
1299   Debug("log-preproc", "log preproc thread is alive ...");
1300 
1301   Log::preproc_notify[idx].lock();
1302 
1303   while (true) {
1304     if (TSSystemState::is_event_system_shut_down()) {
1305       return nullptr;
1306     }
1307     LogConfig *current = static_cast<LogConfig *>(configProcessor.get(log_configid));
1308 
1309     if (likely(current)) {
1310       size_t buffers_preproced = current->log_object_manager.preproc_buffers(idx);
1311 
1312       // config->increment_space_used(bytes_to_disk);
1313       // TODO: the bytes_to_disk should be set to Log
1314 
1315       Debug("log-preproc", "%zu buffers preprocessed from LogConfig %p (refcount=%d) this round", buffers_preproced, current,
1316             current->refcount());
1317 
1318       configProcessor.release(log_configid, current);
1319     }
1320 
1321     // wait for more work; a spurious wake-up is ok since we'll just
1322     // check the queue and find there is nothing to do, then wait
1323     // again.
1324     //
1325     Log::preproc_notify[idx].wait();
1326   }
1327 
1328   /* NOTREACHED */
1329   Log::preproc_notify[idx].unlock();
1330   return nullptr;
1331 }
1332 
1333 void *
flush_thread_main(void *)1334 Log::flush_thread_main(void * /* args ATS_UNUSED */)
1335 {
1336   LogBuffer *logbuffer;
1337   LogFlushData *fdata;
1338   ink_hrtime now, last_time = 0;
1339   int len, total_bytes;
1340   SLL<LogFlushData, LogFlushData::Link_link> link, invert_link;
1341   ProxyMutex *mutex = this_thread()->mutex.get();
1342 
1343   Log::flush_notify->lock();
1344 
1345   while (true) {
1346     if (TSSystemState::is_event_system_shut_down()) {
1347       return nullptr;
1348     }
1349     fdata = static_cast<LogFlushData *>(ink_atomiclist_popall(flush_data_list));
1350 
1351     // invert the list
1352     //
1353     link.head = fdata;
1354     while ((fdata = link.pop())) {
1355       invert_link.push(fdata);
1356     }
1357 
1358     // process each flush data
1359     //
1360     while ((fdata = invert_link.pop())) {
1361       char *buf         = nullptr;
1362       int bytes_written = 0;
1363       LogFile *logfile  = fdata->m_logfile.get();
1364 
1365       if (logfile->m_file_format == LOG_FILE_BINARY) {
1366         logbuffer                      = static_cast<LogBuffer *>(fdata->m_data);
1367         LogBufferHeader *buffer_header = logbuffer->header();
1368 
1369         buf         = reinterpret_cast<char *>(buffer_header);
1370         total_bytes = buffer_header->byte_count;
1371 
1372       } else if (logfile->m_file_format == LOG_FILE_ASCII || logfile->m_file_format == LOG_FILE_PIPE) {
1373         buf         = static_cast<char *>(fdata->m_data);
1374         total_bytes = fdata->m_len;
1375 
1376       } else {
1377         ink_release_assert(!"Unknown file format type!");
1378       }
1379 
1380       // make sure we're open & ready to write
1381       logfile->check_fd();
1382       if (!logfile->is_open()) {
1383         Warning("File:%s was closed, have dropped (%d) bytes.", logfile->get_name(), total_bytes);
1384 
1385         RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_lost_before_written_to_disk_stat, total_bytes);
1386         delete fdata;
1387         continue;
1388       }
1389 
1390       int logfilefd = logfile->get_fd();
1391       // This should always be true because we just checked it.
1392       ink_assert(logfilefd >= 0);
1393 
1394       // write *all* data to target file as much as possible
1395       //
1396       while (total_bytes - bytes_written) {
1397         if (Log::config->logging_space_exhausted) {
1398           Debug("log", "logging space exhausted, failed to write file:%s, have dropped (%d) bytes.", logfile->get_name(),
1399                 (total_bytes - bytes_written));
1400 
1401           RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_lost_before_written_to_disk_stat,
1402                          total_bytes - bytes_written);
1403           break;
1404         }
1405 
1406         len = ::write(logfilefd, &buf[bytes_written], total_bytes - bytes_written);
1407 
1408         if (len < 0) {
1409           Error("Failed to write log to %s: [tried %d, wrote %d, %s]", logfile->get_name(), total_bytes - bytes_written,
1410                 bytes_written, strerror(errno));
1411 
1412           RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_lost_before_written_to_disk_stat,
1413                          total_bytes - bytes_written);
1414           break;
1415         }
1416         Debug("log", "Successfully wrote some stuff to %s", logfile->get_name());
1417         bytes_written += len;
1418       }
1419 
1420       RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_written_to_disk_stat, bytes_written);
1421 
1422       if (logfile->m_log) {
1423         ink_atomic_increment(&logfile->m_log->m_bytes_written, bytes_written);
1424       }
1425 
1426       delete fdata;
1427     }
1428 
1429     // Time to work on periodic events??
1430     //
1431     now = Thread::get_hrtime() / HRTIME_SECOND;
1432     if (now >= last_time + periodic_tasks_interval) {
1433       Debug("log-preproc", "periodic tasks for %" PRId64, (int64_t)now);
1434       periodic_tasks(now);
1435       last_time = Thread::get_hrtime() / HRTIME_SECOND;
1436     }
1437 
1438     // wait for more work; a spurious wake-up is ok since we'll just
1439     // check the queue and find there is nothing to do, then wait
1440     // again.
1441     //
1442     Log::flush_notify->wait();
1443   }
1444 
1445   /* NOTREACHED */
1446   Log::flush_notify->unlock();
1447   return nullptr;
1448 }
1449