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 LogFile.cc
26
27
28 ***************************************************************************/
29
30 #include <vector>
31 #include <string>
32 #include <algorithm>
33
34 #include "tscore/ink_platform.h"
35 #include "tscore/SimpleTokenizer.h"
36 #include "tscore/ink_file.h"
37
38 #include <cerrno>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <libgen.h>
43
44 #include "P_EventSystem.h"
45 #include "I_Machine.h"
46
47 #include "tscore/BaseLogFile.h"
48 #include "LogField.h"
49 #include "LogFilter.h"
50 #include "LogFormat.h"
51 #include "LogBuffer.h"
52 #include "LogFile.h"
53 #include "LogObject.h"
54 #include "LogUtils.h"
55 #include "LogConfig.h"
56 #include "Log.h"
57
58 /*-------------------------------------------------------------------------
59 LogFile::LogFile
60
61 This constructor builds a LogFile object given the path, filename, header,
62 and logfile format type. This is the common way to create a new logfile.
63 -------------------------------------------------------------------------*/
64
LogFile(const char * name,const char * header,LogFileFormat format,uint64_t signature,size_t ascii_buffer_size,size_t max_line_size,int pipe_buffer_size)65 LogFile::LogFile(const char *name, const char *header, LogFileFormat format, uint64_t signature, size_t ascii_buffer_size,
66 size_t max_line_size, int pipe_buffer_size)
67 : m_file_format(format),
68 m_name(ats_strdup(name)),
69 m_header(ats_strdup(header)),
70 m_signature(signature),
71 m_max_line_size(max_line_size),
72 m_pipe_buffer_size(pipe_buffer_size)
73 {
74 if (m_file_format != LOG_FILE_PIPE) {
75 m_log = new BaseLogFile(name, m_signature);
76 m_log->set_hostname(Machine::instance()->hostname);
77 } else {
78 m_log = nullptr;
79 }
80
81 m_fd = -1;
82 m_ascii_buffer_size = (ascii_buffer_size < max_line_size ? max_line_size : ascii_buffer_size);
83
84 Debug("log-file", "exiting LogFile constructor, m_name=%s, this=%p", m_name, this);
85 }
86
87 /*-------------------------------------------------------------------------
88 LogFile::LogFile
89
90 This (copy) constructor builds a LogFile object from another LogFile object.
91 -------------------------------------------------------------------------*/
92
LogFile(const LogFile & copy)93 LogFile::LogFile(const LogFile ©)
94 : RefCountObj(copy),
95 m_file_format(copy.m_file_format),
96 m_name(ats_strdup(copy.m_name)),
97 m_header(ats_strdup(copy.m_header)),
98 m_signature(copy.m_signature),
99 m_ascii_buffer_size(copy.m_ascii_buffer_size),
100 m_max_line_size(copy.m_max_line_size),
101 m_pipe_buffer_size(copy.m_pipe_buffer_size),
102 m_fd(copy.m_fd)
103 {
104 ink_release_assert(m_ascii_buffer_size >= m_max_line_size);
105
106 if (copy.m_log) {
107 m_log = new BaseLogFile(*(copy.m_log));
108 } else {
109 m_log = nullptr;
110 }
111
112 Debug("log-file", "exiting LogFile copy constructor, m_name=%s, this=%p", m_name, this);
113 }
114 /*-------------------------------------------------------------------------
115 LogFile::~LogFile
116 -------------------------------------------------------------------------*/
117
~LogFile()118 LogFile::~LogFile()
119 {
120 Debug("log-file", "entering LogFile destructor, this=%p", this);
121
122 // close_file() checks whether a file is open before attempting to close, so
123 // this is safe to call even if a file had not been opened. Further, calling
124 // close_file() here ensures that we do not leak file descriptors.
125 close_file();
126
127 delete m_log;
128 ats_free(m_header);
129 ats_free(m_name);
130 Debug("log-file", "exiting LogFile destructor, this=%p", this);
131 }
132
133 /*-------------------------------------------------------------------------
134 LogFile::change_name
135 -------------------------------------------------------------------------*/
136
137 void
change_name(const char * new_name)138 LogFile::change_name(const char *new_name)
139 {
140 ats_free(m_name);
141 if (m_log) {
142 m_log->change_name(new_name);
143 }
144 m_name = ats_strdup(new_name);
145 }
146
147 /*-------------------------------------------------------------------------
148 LogFile::change_header
149 -------------------------------------------------------------------------*/
150
151 void
change_header(const char * header)152 LogFile::change_header(const char *header)
153 {
154 ats_free(m_header);
155 m_header = ats_strdup(header);
156 }
157
158 /*-------------------------------------------------------------------------
159 LogFile::open
160
161 Open the logfile for append access. This will create a logfile if the
162 file does not already exist.
163 -------------------------------------------------------------------------*/
164
165 int
open_file()166 LogFile::open_file()
167 {
168 // whatever we want to open should have a name
169 ink_assert(m_name != nullptr);
170
171 // is_open() takes into account if we're using BaseLogFile or a naked fd
172 if (is_open()) {
173 return LOG_FILE_NO_ERROR;
174 }
175
176 bool file_exists = LogFile::exists(m_name);
177
178 if (m_file_format == LOG_FILE_PIPE) {
179 // setup pipe
180 if (mkfifo(m_name, S_IRUSR | S_IWUSR | S_IRGRP) < 0) {
181 if (errno != EEXIST) {
182 Error("Could not create named pipe %s for logging: %s", m_name, strerror(errno));
183 return LOG_FILE_COULD_NOT_CREATE_PIPE;
184 }
185 } else {
186 Debug("log-file", "Created named pipe %s for logging", m_name);
187 }
188
189 // now open the pipe
190 Debug("log-file", "attempting to open pipe %s", m_name);
191 m_fd = ::open(m_name, O_WRONLY | O_NDELAY, 0);
192 if (m_fd < 0) {
193 Debug("log-file", "no readers for pipe %s", m_name);
194 return LOG_FILE_NO_PIPE_READERS;
195 }
196
197 #ifdef F_GETPIPE_SZ
198 // adjust pipe size if necessary
199 if (m_pipe_buffer_size) {
200 long pipe_size = static_cast<long>(fcntl(m_fd, F_GETPIPE_SZ));
201 if (pipe_size == -1) {
202 Error("Get pipe size failed for pipe %s: %s", m_name, strerror(errno));
203 } else {
204 Debug("log-file", "Previous buffer size for pipe %s: %ld", m_name, pipe_size);
205 }
206
207 int ret = fcntl(m_fd, F_SETPIPE_SZ, m_pipe_buffer_size);
208 if (ret == -1) {
209 Error("Set pipe size failed for pipe %s to size %d: %s", m_name, m_pipe_buffer_size, strerror(errno));
210 }
211
212 pipe_size = static_cast<long>(fcntl(m_fd, F_GETPIPE_SZ));
213 if (pipe_size == -1) {
214 Error("Get pipe size after setting it failed for pipe %s: %s", m_name, strerror(errno));
215 } else {
216 Debug("log-file", "New buffer size for pipe %s: %ld", m_name, pipe_size);
217 }
218 }
219 #endif // F_GETPIPE_SZ
220 } else {
221 if (m_log) {
222 int status = m_log->open_file(Log::config->logfile_perm);
223 if (status == BaseLogFile::LOG_FILE_COULD_NOT_OPEN_FILE) {
224 return LOG_FILE_COULD_NOT_OPEN_FILE;
225 }
226 } else {
227 return LOG_FILE_COULD_NOT_OPEN_FILE;
228 }
229 }
230
231 //
232 // If we've opened the file and it didn't already exist, then this is a
233 // "new" file and we need to make some initializations. This is the
234 // time to write any headers and do any one-time initialization of the
235 // file.
236 //
237 if (!file_exists) {
238 if (m_file_format != LOG_FILE_BINARY && m_header && m_log) {
239 Debug("log-file", "writing header to LogFile %s", m_name);
240 writeln(m_header, strlen(m_header), fileno(m_log->m_fp), m_name);
241 }
242 }
243
244 RecIncrRawStat(log_rsb, this_thread()->mutex->thread_holding, log_stat_log_files_open_stat, 1);
245
246 Debug("log", "exiting LogFile::open_file(), file=%s presumably open", m_name);
247 return LOG_FILE_NO_ERROR;
248 }
249
250 /*-------------------------------------------------------------------------
251 LogFile::close
252
253 Close the current logfile.
254 -------------------------------------------------------------------------*/
255
256 void
close_file()257 LogFile::close_file()
258 {
259 if (is_open()) {
260 if (m_file_format == LOG_FILE_PIPE) {
261 if (::close(m_fd)) {
262 Error("Error closing LogFile %s: %s.", m_name, strerror(errno));
263 } else {
264 Debug("log-file", "LogFile %s (fd=%d) is closed", m_name, m_fd);
265 RecIncrRawStat(log_rsb, this_thread()->mutex->thread_holding, log_stat_log_files_open_stat, -1);
266 }
267 m_fd = -1;
268 } else if (m_log) {
269 if (m_log->close_file()) {
270 Error("Error closing LogFile %s: %s.", m_log->get_name(), strerror(errno));
271 } else {
272 Debug("log-file", "LogFile %s is closed", m_log->get_name());
273 RecIncrRawStat(log_rsb, this_thread()->mutex->thread_holding, log_stat_log_files_open_stat, -1);
274 }
275 } else {
276 Warning("LogFile %s is open but was not closed", m_name);
277 }
278 }
279 }
280
281 struct RolledFile {
RolledFileRolledFile282 RolledFile(const std::string &name, time_t mtime) : _name(name), _mtime(mtime) {}
283 std::string _name;
284 time_t _mtime;
285 };
286
287 /**
288 * @brief trim rolled files to max number of rolled files, older first
289 *
290 * @param rolling_max_count - limit to which rolled files will be trimmed
291 * @return true if success, false if failure
292 */
293 bool
trim_rolled(size_t rolling_max_count)294 LogFile::trim_rolled(size_t rolling_max_count)
295 {
296 /* man: "dirname() may modify the contents of path, so it may be
297 * desirable to pass a copy when calling one of these functions." */
298 char *name = ats_strdup(m_name);
299 std::string logfile_dir(::dirname((name)));
300 ats_free(name);
301
302 /* Check logging directory access */
303 int err;
304 do {
305 err = access(logfile_dir.c_str(), R_OK | W_OK | X_OK);
306 } while ((err < 0) && (errno == EINTR));
307
308 if (err < 0) {
309 Error("Error accessing logging directory %s: %s.", logfile_dir.c_str(), strerror(errno));
310 return false;
311 }
312
313 /* Open logging directory */
314 DIR *ld = ::opendir(logfile_dir.c_str());
315 if (ld == nullptr) {
316 Error("Error opening logging directory %s to collect trim candidates: %s.", logfile_dir.c_str(), strerror(errno));
317 return false;
318 }
319
320 /* Collect the rolled file names from the logging directory that match the specified log file name */
321 std::vector<RolledFile> rolled;
322 char path[MAXPATHLEN];
323 struct dirent *entry;
324 while ((entry = readdir(ld))) {
325 struct stat sbuf;
326 snprintf(path, MAXPATHLEN, "%s/%s", logfile_dir.c_str(), entry->d_name);
327 int sret = ::stat(path, &sbuf);
328 if (sret != -1 && S_ISREG(sbuf.st_mode)) {
329 int name_len = strlen(m_name);
330 int path_len = strlen(path);
331 if (path_len > name_len && 0 == ::strncmp(m_name, path, name_len) && LogFile::rolled_logfile(entry->d_name)) {
332 rolled.push_back(RolledFile(path, sbuf.st_mtime));
333 }
334 }
335 }
336
337 ::closedir(ld);
338
339 bool result = true;
340 std::sort(rolled.begin(), rolled.end(), [](const RolledFile &a, const RolledFile &b) { return a._mtime > b._mtime; });
341 if (rolling_max_count < rolled.size()) {
342 for (auto it = rolled.begin() + rolling_max_count; it != rolled.end(); it++) {
343 const RolledFile &file = *it;
344 if (unlink(file._name.c_str()) < 0) {
345 Error("unable to auto-delete rolled logfile %s: %s.", file._name.c_str(), strerror(errno));
346 result = false;
347 } else {
348 Debug("log-file", "rolled logfile, %s, was auto-deleted", file._name.c_str());
349 }
350 }
351 }
352
353 rolled.clear();
354 return result;
355 }
356
357 /*-------------------------------------------------------------------------
358 * LogFile::roll
359 * This function is called by a LogObject to roll its files.
360 *
361 * Return 1 if file rolled, 0 otherwise
362 -------------------------------------------------------------------------*/
363 int
roll(long interval_start,long interval_end,bool reopen_after_rolling)364 LogFile::roll(long interval_start, long interval_end, bool reopen_after_rolling)
365 {
366 if (m_log) {
367 // Due to commit 346b419 the BaseLogFile::close_file() is no longer called within BaseLogFile::roll().
368 // For diagnostic log files, the rolling is implemented by renaming and destroying the BaseLogFile object
369 // and then creating a new BaseLogFile object with the original filename. Due to possible race conditions
370 // the old/new object swap happens within lock/unlock calls within Diags.cc.
371 // For logging log files, the rolling is implemented by renaming the original file and closing it.
372 // Afterwards, the LogFile object will re-open a new file with the original file name using the original object.
373 // There is no need to protect against contention since the open/close/writes are all executed under a
374 // single log flush thread.
375 // Since these two methods of using BaseLogFile are not compatible, we perform the logging log file specific
376 // close file operation here within the containing LogFile object.
377 if (m_log->roll(interval_start, interval_end)) {
378 if (m_log->close_file()) {
379 Error("Error closing LogFile %s: %s.", m_log->get_name(), strerror(errno));
380 }
381
382 if (reopen_after_rolling) {
383 /* If we re-open now log file will be created even if there is nothing being logged */
384 m_log->open_file();
385 }
386
387 return 1;
388 }
389 }
390
391 return 0;
392 }
393
394 /*-------------------------------------------------------------------------
395 LogFile::reopen_if_moved
396
397 Check whether the file at the log's filename exists and, if not, close
398 the current file descriptor and reopen it. This function can be used to
399 facilitate external log rotation mechanisms which will move the original
400 file to a rolled filename. Logging will happen to that same file
401 descriptor until this function is called, at which point the non-existent
402 original file will be detected, the file descriptor will be closed, and
403 the log file will be re-opened.
404
405 Returns True if the file was re-opened, false otherwise.
406 -------------------------------------------------------------------------*/
407 bool
reopen_if_moved()408 LogFile::reopen_if_moved()
409 {
410 if (!m_name) {
411 return false;
412 }
413 if (LogFile::exists(m_name)) {
414 return false;
415 }
416
417 // Both of the following log if there are problems.
418 close_file();
419 open_file();
420 return true;
421 }
422
423 /*-------------------------------------------------------------------------
424 LogFile::preproc_and_try_delete
425
426 preprocess the given buffer data before write to target file
427 and try to delete it when its reference become zero.
428 -------------------------------------------------------------------------*/
429 int
preproc_and_try_delete(LogBuffer * lb)430 LogFile::preproc_and_try_delete(LogBuffer *lb)
431 {
432 int ret = -1;
433 LogBufferHeader *buffer_header;
434
435 if (lb == nullptr) {
436 Note("Cannot write LogBuffer to LogFile %s; LogBuffer is NULL", m_name);
437 return -1;
438 }
439
440 ink_atomic_increment(&lb->m_references, 1);
441
442 if ((buffer_header = lb->header()) == nullptr) {
443 Note("Cannot write LogBuffer to LogFile %s; LogBufferHeader is NULL", m_name);
444 goto done;
445 }
446 if (buffer_header->entry_count == 0) {
447 // no bytes to write
448 Note("LogBuffer with 0 entries for LogFile %s, nothing to write", m_name);
449 goto done;
450 }
451
452 //
453 // If the start time for this file has yet to be established, then grab
454 // the low_timestamp from the given LogBuffer. Then, we always set the
455 // end time to the high_timestamp, so it's always up to date.
456 //
457 if (m_log) {
458 if (!m_log->m_start_time) {
459 m_log->m_start_time = buffer_header->low_timestamp;
460 }
461 m_log->m_end_time = buffer_header->high_timestamp;
462 }
463
464 if (m_file_format == LOG_FILE_BINARY) {
465 //
466 // Ok, now we need to write the binary buffer to the file, and we
467 // can do so in one swift write. The question is, do we write the
468 // LogBufferHeader with each buffer or not? The answer is yes.
469 // Even though we'll be puttint down redundant data (things that
470 // don't change between buffers), it's not worth trying to separate
471 // out the buffer-dependent data from the buffer-independent data.
472 //
473 LogFlushData *flush_data = new LogFlushData(this, lb);
474
475 ProxyMutex *mutex = this_thread()->mutex.get();
476
477 RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_num_flush_to_disk_stat, lb->header()->entry_count);
478
479 RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_flush_to_disk_stat, lb->header()->byte_count);
480
481 ink_atomiclist_push(Log::flush_data_list, flush_data);
482
483 Log::flush_notify->signal();
484
485 //
486 // LogBuffer will be deleted in flush thread
487 //
488 return 0;
489 } else if (m_file_format == LOG_FILE_ASCII || m_file_format == LOG_FILE_PIPE) {
490 write_ascii_logbuffer3(buffer_header);
491 ret = 0;
492 } else {
493 Note("Cannot write LogBuffer to LogFile %s; invalid file format: %d", m_name, m_file_format);
494 }
495
496 done:
497 LogBuffer::destroy(lb);
498 return ret;
499 }
500
501 /*-------------------------------------------------------------------------
502 LogFile::write_ascii_logbuffer
503
504 This routine takes the given LogBuffer and writes it (in ASCII) to the
505 given file descriptor. Written as a stand-alone function, it can be
506 called from either the local LogBuffer::write routine from inside of the
507 proxy, or from an external program (since it is a static function). The
508 return value is the number of bytes written.
509 -------------------------------------------------------------------------*/
510
511 int
write_ascii_logbuffer(LogBufferHeader * buffer_header,int fd,const char * path,const char * alt_format)512 LogFile::write_ascii_logbuffer(LogBufferHeader *buffer_header, int fd, const char *path, const char *alt_format)
513 {
514 ink_assert(buffer_header != nullptr);
515 ink_assert(fd >= 0);
516
517 char fmt_buf[LOG_MAX_FORMATTED_BUFFER];
518 char fmt_line[LOG_MAX_FORMATTED_LINE];
519 LogBufferIterator iter(buffer_header);
520 LogEntryHeader *entry_header;
521 int fmt_buf_bytes = 0;
522 int fmt_line_bytes = 0;
523 int bytes = 0;
524
525 LogFormatType format_type;
526 char *fieldlist_str;
527 char *printf_str;
528
529 switch (buffer_header->version) {
530 case LOG_SEGMENT_VERSION:
531 format_type = static_cast<LogFormatType>(buffer_header->format_type);
532
533 fieldlist_str = buffer_header->fmt_fieldlist();
534 printf_str = buffer_header->fmt_printf();
535 break;
536
537 default:
538 Note("Invalid LogBuffer version %d in write_ascii_logbuffer; "
539 "current version is %d",
540 buffer_header->version, LOG_SEGMENT_VERSION);
541 return 0;
542 }
543
544 while ((entry_header = iter.next())) {
545 fmt_line_bytes = LogBuffer::to_ascii(entry_header, format_type, &fmt_line[0], LOG_MAX_FORMATTED_LINE, fieldlist_str, printf_str,
546 buffer_header->version, alt_format);
547 ink_assert(fmt_line_bytes > 0);
548
549 if (fmt_line_bytes > 0) {
550 if ((fmt_line_bytes + fmt_buf_bytes) >= LOG_MAX_FORMATTED_BUFFER) {
551 if (!Log::config->logging_space_exhausted) {
552 bytes += writeln(fmt_buf, fmt_buf_bytes, fd, path);
553 }
554 fmt_buf_bytes = 0;
555 }
556 ink_assert(fmt_buf_bytes < LOG_MAX_FORMATTED_BUFFER);
557 ink_assert(fmt_line_bytes < LOG_MAX_FORMATTED_BUFFER - fmt_buf_bytes);
558 memcpy(&fmt_buf[fmt_buf_bytes], fmt_line, fmt_line_bytes);
559 fmt_buf_bytes += fmt_line_bytes;
560 ink_assert(fmt_buf_bytes < LOG_MAX_FORMATTED_BUFFER);
561 fmt_buf[fmt_buf_bytes] = '\n'; // keep entries separate
562 fmt_buf_bytes += 1;
563 }
564 }
565 if (fmt_buf_bytes > 0) {
566 if (!Log::config->logging_space_exhausted) {
567 ink_assert(fmt_buf_bytes < LOG_MAX_FORMATTED_BUFFER);
568 bytes += writeln(fmt_buf, fmt_buf_bytes, fd, path);
569 }
570 }
571
572 return bytes;
573 }
574
575 int
write_ascii_logbuffer3(LogBufferHeader * buffer_header,const char * alt_format)576 LogFile::write_ascii_logbuffer3(LogBufferHeader *buffer_header, const char *alt_format)
577 {
578 Debug("log-file",
579 "entering LogFile::write_ascii_logbuffer3 for %s "
580 "(this=%p)",
581 m_name, this);
582 ink_assert(buffer_header != nullptr);
583
584 ProxyMutex *mutex = this_thread()->mutex.get();
585 LogBufferIterator iter(buffer_header);
586 LogEntryHeader *entry_header;
587 int fmt_entry_count = 0;
588 int fmt_buf_bytes = 0;
589 int total_bytes = 0;
590
591 LogFormatType format_type;
592 char *fieldlist_str;
593 char *printf_str;
594 char *ascii_buffer;
595
596 switch (buffer_header->version) {
597 case LOG_SEGMENT_VERSION:
598 format_type = static_cast<LogFormatType>(buffer_header->format_type);
599 fieldlist_str = buffer_header->fmt_fieldlist();
600 printf_str = buffer_header->fmt_printf();
601 break;
602
603 default:
604 Note("Invalid LogBuffer version %d in write_ascii_logbuffer; "
605 "current version is %d",
606 buffer_header->version, LOG_SEGMENT_VERSION);
607 return 0;
608 }
609
610 while ((entry_header = iter.next())) {
611 fmt_entry_count = 0;
612 fmt_buf_bytes = 0;
613
614 if (m_file_format == LOG_FILE_PIPE) {
615 ascii_buffer = static_cast<char *>(ats_malloc(m_max_line_size));
616 } else {
617 ascii_buffer = static_cast<char *>(ats_malloc(m_ascii_buffer_size));
618 }
619
620 // fill the buffer with as many records as possible
621 //
622 do {
623 if (entry_header->entry_len >= m_max_line_size) {
624 Warning("Log is too long(%" PRIu32 "), it would be truncated. max_len:%zu", entry_header->entry_len, m_max_line_size);
625 }
626
627 int bytes = LogBuffer::to_ascii(entry_header, format_type, &ascii_buffer[fmt_buf_bytes], m_max_line_size - 1, fieldlist_str,
628 printf_str, buffer_header->version, alt_format);
629
630 if (bytes > 0) {
631 fmt_buf_bytes += bytes;
632 ascii_buffer[fmt_buf_bytes] = '\n';
633 ++fmt_buf_bytes;
634 ++fmt_entry_count;
635 } else {
636 Note("Failed to convert LogBuffer to ascii, have dropped (%" PRIu32 ") bytes.", entry_header->entry_len);
637
638 RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_num_lost_before_flush_to_disk_stat, fmt_entry_count);
639
640 RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_lost_before_flush_to_disk_stat, fmt_buf_bytes);
641 }
642 // if writing to a pipe, fill the buffer with a single
643 // record to avoid as much as possible overflowing the
644 // pipe buffer
645 //
646 if (m_file_format == LOG_FILE_PIPE) {
647 break;
648 }
649
650 if (m_ascii_buffer_size - fmt_buf_bytes < m_max_line_size) {
651 break;
652 }
653 } while ((entry_header = iter.next()));
654
655 // send the buffer to flush thread
656 //
657 LogFlushData *flush_data = new LogFlushData(this, ascii_buffer, fmt_buf_bytes);
658
659 RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_num_flush_to_disk_stat, fmt_entry_count);
660
661 RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_flush_to_disk_stat, fmt_buf_bytes);
662
663 ink_atomiclist_push(Log::flush_data_list, flush_data);
664
665 Log::flush_notify->signal();
666
667 total_bytes += fmt_buf_bytes;
668 }
669
670 return total_bytes;
671 }
672
673 bool
rolled_logfile(char * file)674 LogFile::rolled_logfile(char *file)
675 {
676 return BaseLogFile::rolled_logfile(file);
677 }
678
679 bool
exists(const char * pathname)680 LogFile::exists(const char *pathname)
681 {
682 return BaseLogFile::exists(pathname);
683 }
684
685 /*-------------------------------------------------------------------------
686 LogFile::writeln
687
688 This function will make sure the following data is written to the
689 output file (m_fd) with a trailing newline.
690 -------------------------------------------------------------------------*/
691
692 int
writeln(char * data,int len,int fd,const char * path)693 LogFile::writeln(char *data, int len, int fd, const char *path)
694 {
695 int total_bytes = 0;
696
697 if (len > 0 && data && fd >= 0) {
698 struct iovec wvec[2];
699 memset(&wvec, 0, sizeof(iovec));
700 memset(&wvec[1], 0, sizeof(iovec));
701 int bytes_this_write, vcnt = 1;
702
703 #if defined(solaris)
704 wvec[0].iov_base = (caddr_t)data;
705 #else
706 wvec[0].iov_base = (void *)data;
707 #endif
708 wvec[0].iov_len = static_cast<size_t>(len);
709
710 if (data[len - 1] != '\n') {
711 #if defined(solaris)
712 wvec[1].iov_base = (caddr_t) "\n";
713 #else
714 wvec[1].iov_base = (void *)"\n";
715 #endif
716 wvec[1].iov_len = static_cast<size_t>(1);
717 vcnt++;
718 }
719
720 if ((bytes_this_write = static_cast<int>(::writev(fd, (const struct iovec *)wvec, vcnt))) < 0) {
721 Warning("An error was encountered in writing to %s: %s.", ((path) ? path : "logfile"), strerror(errno));
722 } else {
723 total_bytes = bytes_this_write;
724 }
725 }
726 return total_bytes;
727 }
728
729 /*-------------------------------------------------------------------------
730 LogFile::check_fd
731
732 This routine will occasionally stat the current logfile to make sure that
733 it really does exist. The easiest way to do this is to close the file
734 and re-open it, which will create the file if it doesn't already exist.
735
736 Failure to open the logfile will generate a manager alarm and a Warning.
737 -------------------------------------------------------------------------*/
738
739 void
check_fd()740 LogFile::check_fd()
741 {
742 static bool failure_last_call = false;
743 static unsigned stat_check_count = 1;
744
745 if ((stat_check_count % Log::config->file_stat_frequency) == 0) {
746 //
747 // It's time to see if the file really exists. If we can't see
748 // the file (via access), then we'll close our descriptor and
749 // attempt to re-open it, which will create the file if it's not
750 // there.
751 //
752 if (m_name && !LogFile::exists(m_name)) {
753 close_file();
754 }
755 stat_check_count = 0;
756 }
757 stat_check_count++;
758
759 int err = open_file();
760 // XXX if open_file() returns, LOG_FILE_FILESYSTEM_CHECKS_FAILED, raise a more informative alarm ...
761 if (err != LOG_FILE_NO_ERROR && err != LOG_FILE_NO_PIPE_READERS) {
762 if (!failure_last_call) {
763 LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, "Traffic Server could not open logfile %s.", m_name);
764 Warning("Traffic Server could not open logfile %s: %s.", m_name, strerror(errno));
765 }
766 failure_last_call = true;
767 return;
768 }
769
770 failure_last_call = false;
771 }
772
773 void
display(FILE * fd)774 LogFile::display(FILE *fd)
775 {
776 fprintf(fd, "Logfile: %s, %s\n", get_name(), (is_open()) ? "file is open" : "file is not open");
777 }
778
779 bool
is_open()780 LogFile::is_open()
781 {
782 if (m_file_format == LOG_FILE_PIPE) {
783 return m_fd >= 0;
784 } else {
785 return m_log && m_log->is_open();
786 }
787 }
788
789 /*
790 * Returns the fd of the entity (pipe or regular file ) that this object is
791 * representing
792 *
793 * Returns -1 on error, the correct fd otherwise
794 */
795 int
get_fd()796 LogFile::get_fd()
797 {
798 if (m_file_format == LOG_FILE_PIPE) {
799 return m_fd;
800 } else if (m_log && m_log->m_fp) {
801 return fileno(m_log->m_fp);
802 } else {
803 return -1;
804 }
805 }
806