1/*
2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24#include "precompiled.hpp"
25#include "logging/logDecorations.hpp"
26#include "logging/logFileStreamOutput.hpp"
27#include "logging/logLevel.hpp"
28#include "logging/logMessageBuffer.hpp"
29#include "logging/logOutput.hpp"
30#include "logging/logTag.hpp"
31#include "logging/logTagSet.hpp"
32#include "logging/logTagSetDescriptions.hpp"
33#include "memory/allocation.inline.hpp"
34#include "prims/jvm.h"
35#include "utilities/ostream.hpp"
36
37LogTagSet*  LogTagSet::_list      = NULL;
38size_t      LogTagSet::_ntagsets  = 0;
39
40// This constructor is called only during static initialization.
41// See the declaration in logTagSet.hpp for more information.
42LogTagSet::LogTagSet(PrefixWriter prefix_writer, LogTagType t0, LogTagType t1, LogTagType t2, LogTagType t3, LogTagType t4)
43    : _next(_list), _write_prefix(prefix_writer) {
44  _tag[0] = t0;
45  _tag[1] = t1;
46  _tag[2] = t2;
47  _tag[3] = t3;
48  _tag[4] = t4;
49  for (_ntags = 0; _ntags < LogTag::MaxTags && _tag[_ntags] != LogTag::__NO_TAG; _ntags++) {
50  }
51  _list = this;
52  _ntagsets++;
53
54  // Set the default output to warning and error level for all new tagsets.
55  _output_list.set_output_level(&StdoutLog, LogLevel::Default);
56}
57
58void LogTagSet::update_decorators(const LogDecorators& decorator) {
59  LogDecorators new_decorators = decorator;
60  for (LogOutputList::Iterator it = _output_list.iterator(); it != _output_list.end(); it++) {
61    new_decorators.combine_with((*it)->decorators());
62  }
63  _decorators = new_decorators;
64}
65
66bool LogTagSet::has_output(const LogOutput* output) {
67  for (LogOutputList::Iterator it = _output_list.iterator(); it != _output_list.end(); it++) {
68    if (*it == output) {
69      return true;
70    }
71  }
72  return false;
73}
74
75void LogTagSet::log(LogLevelType level, const char* msg) {
76  LogDecorations decorations(level, *this, _decorators);
77  for (LogOutputList::Iterator it = _output_list.iterator(level); it != _output_list.end(); it++) {
78    (*it)->write(decorations, msg);
79  }
80}
81
82void LogTagSet::log(const LogMessageBuffer& msg) {
83  LogDecorations decorations(LogLevel::Invalid, *this, _decorators);
84  for (LogOutputList::Iterator it = _output_list.iterator(msg.least_detailed_level()); it != _output_list.end(); it++) {
85    (*it)->write(msg.iterator(it.level(), decorations));
86  }
87}
88
89int LogTagSet::label(char* buf, size_t len, const char* separator) const {
90  int tot_written = 0;
91  for (size_t i = 0; i < _ntags; i++) {
92    int written = jio_snprintf(buf + tot_written, len - tot_written, "%s%s",
93                               (i == 0 ? "" : separator),
94                               LogTag::name(_tag[i]));
95    if (written < 0) {
96      return -1;
97    }
98    tot_written += written;
99  }
100  return tot_written;
101}
102
103void LogTagSet::write(LogLevelType level, const char* fmt, ...) {
104  va_list args;
105  va_start(args, fmt);
106  vwrite(level, fmt, args);
107  va_end(args);
108}
109
110const size_t vwrite_buffer_size = 512;
111
112void LogTagSet::vwrite(LogLevelType level, const char* fmt, va_list args) {
113  assert(level >= LogLevel::First && level <= LogLevel::Last, "Log level:%d is incorrect", level);
114  char buf[vwrite_buffer_size];
115  va_list saved_args;           // For re-format on buf overflow.
116  va_copy(saved_args, args);
117  size_t prefix_len = _write_prefix(buf, sizeof(buf));
118  // Check that string fits in buffer; resize buffer if necessary
119  int ret;
120  if (prefix_len < vwrite_buffer_size) {
121    ret = os::log_vsnprintf(buf + prefix_len, sizeof(buf) - prefix_len, fmt, args);
122  } else {
123    // Buffer too small. Just call printf to find out the length for realloc below.
124    ret = os::log_vsnprintf(buf, sizeof(buf), fmt, args);
125  }
126  assert(ret >= 0, "Log message buffer issue");
127  if ((size_t)ret >= sizeof(buf)) {
128    size_t newbuf_len = prefix_len + ret + 1;
129    char* newbuf = NEW_C_HEAP_ARRAY(char, newbuf_len, mtLogging);
130    prefix_len = _write_prefix(newbuf, newbuf_len);
131    ret = os::log_vsnprintf(newbuf + prefix_len, newbuf_len - prefix_len, fmt, saved_args);
132    assert(ret >= 0, "Log message buffer issue");
133    log(level, newbuf);
134    FREE_C_HEAP_ARRAY(char, newbuf);
135  } else {
136    log(level, buf);
137  }
138  va_end(saved_args);
139}
140
141static const size_t TagSetBufferSize = 128;
142
143void LogTagSet::describe_tagsets(outputStream* out) {
144  out->print_cr("Described tag combinations:");
145  for (const LogTagSetDescription* d = tagset_descriptions; d->tagset != NULL; d++) {
146    char buf[TagSetBufferSize];
147    d->tagset->label(buf, sizeof(buf), "+");
148    out->print_cr(" %s: %s", buf, d->descr);
149  }
150}
151
152static int qsort_strcmp(const void* a, const void* b) {
153  return strcmp((*(const char**)a), (*(const char**)b));
154}
155
156void LogTagSet::list_all_tagsets(outputStream* out) {
157  char** tagset_labels = NEW_C_HEAP_ARRAY(char*, _ntagsets, mtLogging);
158
159  // Generate the list of tagset labels
160  size_t idx = 0;
161  for (LogTagSet* ts = first(); ts != NULL; ts = ts->next()) {
162    char buf[TagSetBufferSize];
163    ts->label(buf, sizeof(buf), "+");
164    tagset_labels[idx++] = os::strdup_check_oom(buf, mtLogging);
165  }
166  assert(idx == _ntagsets, "_ntagsets and list of tagsets not in sync");
167
168  // Sort them lexicographically
169  qsort(tagset_labels, _ntagsets, sizeof(*tagset_labels), qsort_strcmp);
170
171  // Print and then free the labels
172  out->print("All available tag sets: ");
173  for (idx = 0; idx < _ntagsets; idx++) {
174    out->print("%s%s", (idx == 0 ? "" : ", "), tagset_labels[idx]);
175    os::free(tagset_labels[idx]);
176  }
177  out->cr();
178  FREE_C_HEAP_ARRAY(char*, tagset_labels);
179}
180
181