This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-doris.git
The following commit(s) were added to refs/heads/master by this push:
new ca8268f [Feature] Extend logger interface, support structured log
output (#6600)
ca8268f is described below
commit ca8268f1c9669aeca510ea705e146d3a4a44a983
Author: ccoffline <[email protected]>
AuthorDate: Sun Nov 7 17:39:53 2021 +0800
[Feature] Extend logger interface, support structured log output (#6600)
Support structured logging.
---
be/src/common/logconfig.cpp | 17 ++++
be/src/runtime/plan_fragment_executor.cpp | 25 +++---
be/src/util/logging.h | 90 ++++++++++++++++++++++
.../doris/common/logger/DefaultDorisLogger.java | 55 +++++++++++++
.../apache/doris/common/logger/LoggerProvider.java | 51 ++++++++++++
.../org/apache/doris/common/logger/TagKey.java | 27 +++++++
.../apache/doris/common/logger/TaggableLogger.java | 64 +++++++++++++++
.../doris/common/logger/TaggedLogFormat.java | 26 +++++++
.../java/org/apache/doris/common/logger/Tags.java | 24 ++++++
9 files changed, 366 insertions(+), 13 deletions(-)
diff --git a/be/src/common/logconfig.cpp b/be/src/common/logconfig.cpp
index e3463f1..49684ac 100644
--- a/be/src/common/logconfig.cpp
+++ b/be/src/common/logconfig.cpp
@@ -159,4 +159,21 @@ std::string FormatTimestampForLog(MicrosecondsInt64
micros_since_epoch) {
tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec,
usecs);
}
+/// Custom your log format here
+void TaggableLogger::flush() {
+ _stream << _message;
+ Tags *head = _tags;
+ Tags *next;
+ while (head) {
+ next = head->next;
+ _stream << "|" << head->key << "=" << head->value;
+ delete head;
+ head = next;
+ }
+}
+
+/// Modify these tag names to suit your log format and collector.
+const std::string TaggableLogger::QUERY_ID = "query_id";
+const std::string TaggableLogger::INSTANCE_ID = "instance_id";
+
} // namespace doris
diff --git a/be/src/runtime/plan_fragment_executor.cpp
b/be/src/runtime/plan_fragment_executor.cpp
index d0f2cb2..670248e 100644
--- a/be/src/runtime/plan_fragment_executor.cpp
+++ b/be/src/runtime/plan_fragment_executor.cpp
@@ -21,7 +21,6 @@
#include <unordered_map>
-#include "common/logging.h"
#include "common/object_pool.h"
#include "exec/data_sink.h"
#include "exec/exchange_node.h"
@@ -40,7 +39,7 @@
#include "util/mem_info.h"
#include "util/parse_util.h"
#include "util/pretty_printer.h"
-#include "util/uid_util.h"
+#include "util/logging.h"
namespace doris {
@@ -70,9 +69,9 @@ Status PlanFragmentExecutor::prepare(const
TExecPlanFragmentParams& request,
const TPlanFragmentExecParams& params = request.params;
_query_id = params.query_id;
- LOG(INFO) << "Prepare(): query_id=" << print_id(_query_id)
- << " fragment_instance_id=" <<
print_id(params.fragment_instance_id)
- << " backend_num=" << request.backend_num;
+ TAG(LOG(INFO)).log("PlanFragmentExecutor::prepare")
+ .query_id(_query_id).instance_id(params.fragment_instance_id)
+ .tag("backend_num", std::to_string(request.backend_num));
// VLOG_CRITICAL << "request:\n" <<
apache::thrift::ThriftDebugString(request);
const TQueryGlobals& query_globals =
@@ -223,10 +222,10 @@ Status PlanFragmentExecutor::prepare(const
TExecPlanFragmentParams& request,
}
Status PlanFragmentExecutor::open() {
- LOG(INFO) << "Open(): fragment_instance_id="
- << print_id(_runtime_state->fragment_instance_id())
- << ", Using query memory limit: "
- <<
PrettyPrinter::print(_runtime_state->fragment_mem_tracker()->limit(),
TUnit::BYTES);
+ int64_t mem_limit = _runtime_state->fragment_mem_tracker()->limit();
+ TAG(LOG(INFO)).log("PlanFragmentExecutor::open, using query memory limit:
" + PrettyPrinter::print(mem_limit, TUnit::BYTES))
+
.query_id(_query_id).instance_id(_runtime_state->fragment_instance_id())
+ .tag("mem_limit", std::to_string(mem_limit));
// we need to start the profile-reporting thread before calling Open(),
since it
// may block
@@ -445,8 +444,8 @@ Status PlanFragmentExecutor::get_next(RowBatch** batch) {
update_status(status);
if (_done) {
- LOG(INFO) << "Finished executing fragment query_id=" <<
print_id(_query_id)
- << " instance_id=" <<
print_id(_runtime_state->fragment_instance_id());
+ TAG(LOG(INFO)).log("PlanFragmentExecutor::get_next finished")
+
.query_id(_query_id).instance_id(_runtime_state->fragment_instance_id());
// Query is done, return the thread token
stop_report_thread();
send_report(true);
@@ -504,8 +503,8 @@ void PlanFragmentExecutor::update_status(const Status&
new_status) {
}
void PlanFragmentExecutor::cancel() {
- LOG(INFO) << "cancel(): fragment_instance_id="
- << print_id(_runtime_state->fragment_instance_id());
+ TAG(LOG(INFO)).log("PlanFragmentExecutor::cancel")
+
.query_id(_query_id).instance_id(_runtime_state->fragment_instance_id());
DCHECK(_prepared);
_runtime_state->set_is_cancelled(true);
_runtime_state->exec_env()->stream_mgr()->cancel(_runtime_state->fragment_instance_id());
diff --git a/be/src/util/logging.h b/be/src/util/logging.h
index 27d7e82..ab23922 100644
--- a/be/src/util/logging.h
+++ b/be/src/util/logging.h
@@ -25,6 +25,7 @@
#include <string>
#include "common/logging.h"
+#include "util/uid_util.h"
#include "gutil/walltime.h"
namespace doris {
@@ -88,6 +89,95 @@ private:
std::atomic<Aws::Utils::Logging::LogLevel> _log_level;
};
+/// Wrap a glog stream and tag on the log. usage:
+/// TAG(LOG(INFO)).tag("query_id", queryId).log("here is an info for a
query");
+///
+/// TAG is the macro to TaggableLogger, which use method tag(key, value) to
add tags
+/// and log(fmt, ...) to flush and emit the log. Usually the tag key is
determined,
+/// like "query_id", so we use specified tag methods more often, like
query_id(id).
+/// You can add a new tag method if needed.
+///
+/// You can custom your tagged logging format in logconfig.cpp, the default is
like
+/// "#message#|k1=v1|k2=v2". You can also custom all the tag names. For
example, if
+/// you wish to use camelCase style, just set tag name constants like QUERY_ID
to
+/// "queryId". The constants is also listed in logconfig.cpp.
+///
+/// The transfer from the variable of tag method to string is immediate. If a
tagged
+/// logging has time-consuming to-string procedure and is logged to VLOG(10)
which
+/// may not be processed, check VLOG_IS_ON(n) first.
+#define TAG doris::TaggableLogger
+
+class TaggableLogger {
+public:
+ TaggableLogger(std::ostream& _stream) : _stream(_stream), _tags(nullptr)
{};
+
+ ~TaggableLogger() {
+ flush();
+ }
+
+ void flush();
+
+ TaggableLogger& log(std::string&& message) {
+ _message = std::move(message);
+ return *this;
+ }
+
+ inline TaggableLogger& tag(const std::string& key, const std::string&
value) {
+ _tags = new Tags(key, value, _tags);
+ return *this;
+ }
+
+ inline TaggableLogger& tag(const std::string& key, std::string&& value) {
+ _tags = new Tags(key, std::move(value), _tags);
+ return *this;
+ }
+
+private:
+ std::ostream& _stream;
+ std::string _message;
+
+ struct Tags {
+ const std::string& key;
+ const std::string value;
+ Tags* next;
+
+ Tags(const std::string& key, const std::string& value, Tags* next) :
key(key), value(value), next(next) {}
+ Tags(const std::string& key, std::string&& value, Tags* next) :
key(key), value(std::move(value)), next(next) {}
+ };
+
+ Tags* _tags;
+
+public:
+ // add tag method here
+ const static std::string QUERY_ID;
+
+ TaggableLogger& query_id(const std::string& query_id) {
+ return tag(QUERY_ID, query_id);
+ }
+
+ TaggableLogger& query_id(const TUniqueId& query_id) {
+ return tag(QUERY_ID, print_id(query_id));
+ }
+
+ TaggableLogger& query_id(const PUniqueId& query_id) {
+ return tag(QUERY_ID, print_id(query_id));
+ }
+
+ const static std::string INSTANCE_ID;
+
+ TaggableLogger& instance_id(const std::string& instance_id) {
+ return tag(INSTANCE_ID, instance_id);
+ }
+
+ TaggableLogger& instance_id(const TUniqueId& instance_id) {
+ return tag(INSTANCE_ID, print_id(instance_id));
+ }
+
+ TaggableLogger& instance_id(const PUniqueId& instance_id) {
+ return tag(INSTANCE_ID, print_id(instance_id));
+ }
+};
+
} // namespace doris
#endif // DORIS_BE_SRC_COMMON_UTIL_LOGGING_H
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/logger/DefaultDorisLogger.java
b/fe/fe-core/src/main/java/org/apache/doris/common/logger/DefaultDorisLogger.java
new file mode 100644
index 0000000..7be90df
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/common/logger/DefaultDorisLogger.java
@@ -0,0 +1,55 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.common.logger;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.spi.ExtendedLogger;
+import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;
+
+public class DefaultDorisLogger extends ExtendedLoggerWrapper implements
TaggableLogger {
+
+ private final TaggedLogFormat format;
+ private final ThreadLocal<Tags> tags;
+
+ public DefaultDorisLogger(ExtendedLogger logger, TaggedLogFormat format) {
+ super(logger, logger.getName(), logger.getMessageFactory());
+ this.format = format;
+ this.tags = ThreadLocal.withInitial(Tags::new);
+ }
+
+ @Override
+ public TaggableLogger tag(String key, Object value) {
+ Tags head = this.tags.get();
+ Tags tag = new Tags();
+ tag.key = key;
+ tag.value = value;
+ tag.next = head.next;
+ head.next = tag;
+ return this;
+ }
+
+ @Override
+ public void logMessage(String fqcn, Level level, Marker marker, Message
message, Throwable t) {
+ Tags tags = this.tags.get();
+ Message m = tags.next == null ? message :
format.getTaggedMessage(message, tags.next);
+ super.logMessage(fqcn, level, marker, m, t);
+ tags.next = null;
+ }
+}
\ No newline at end of file
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/logger/LoggerProvider.java
b/fe/fe-core/src/main/java/org/apache/doris/common/logger/LoggerProvider.java
new file mode 100644
index 0000000..149445d
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/common/logger/LoggerProvider.java
@@ -0,0 +1,51 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.common.logger;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.ExtendedLogger;
+
+public class LoggerProvider {
+
+ public static TaggableLogger getLogger(Class<?> clazz) {
+ return wrap(LogManager.getLogger(clazz));
+ }
+
+ public static TaggableLogger getLogger(String name) {
+ return wrap(LogManager.getLogger(name));
+ }
+
+ public static TaggableLogger wrap(Logger logger) {
+ return new DefaultDorisLogger((ExtendedLogger) logger,
getTaggedLogFormat());
+ }
+
+ // custom your log format here
+ public static TaggedLogFormat getTaggedLogFormat() {
+ return (message, tags) -> {
+ StringBuilder builder = new
StringBuilder(message.getFormattedMessage());
+ while (tags != null) {
+
builder.append('|').append(tags.key).append('=').append(tags.value);
+ tags = tags.next;
+ }
+ return new SimpleMessage(builder);
+ };
+ }
+
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/logger/TagKey.java
b/fe/fe-core/src/main/java/org/apache/doris/common/logger/TagKey.java
new file mode 100644
index 0000000..f1c2ad2
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/logger/TagKey.java
@@ -0,0 +1,27 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.common.logger;
+
+/**
+ * Modify these tag key name to suit your log format and collector.
+ */
+public class TagKey {
+
+ public static final String QUERY_ID = "query_id";
+
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/logger/TaggableLogger.java
b/fe/fe-core/src/main/java/org/apache/doris/common/logger/TaggableLogger.java
new file mode 100644
index 0000000..43d069a
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/common/logger/TaggableLogger.java
@@ -0,0 +1,64 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.common.logger;
+
+import org.apache.doris.common.util.DebugUtil;
+import org.apache.doris.proto.PUniqueId;
+import org.apache.doris.thrift.TUniqueId;
+import org.apache.logging.log4j.Logger;
+
+import java.util.UUID;
+
+/**
+ * Wrap a log4j Logger and tag on the log. usage:
+ * LOG.tag("query_id", queryId).info("here is an info for a query");
+ *
+ * TaggableLogger extends log4j Logger, so it's fully compatible with the usual
+ * one. You can use method tag(key, value) to add tags and log all the tags and
+ * message when log method is called, like info(fmt, ...). Usually the tag key
is
+ * determined, like "query_id", so we use specified tag methods more often,
like
+ * query_id(id). You can add a new tag method to TaggableLogger if needed.
+ *
+ * You can custom your tagged logging format in
LoggerProvider.getTaggedLogFormat,
+ * the default is like "#message#|k1=v1|k2=v2". You can also custom all the tag
+ * names in TagKey. For example, if you wish to use camelCase style, just set
tag
+ * name constants like QUERY_ID to "queryId".
+ *
+ * The transfer from the variable of tag method to string is immediate. If a
tagged
+ * logging has time-consuming to-string procedure and is at debug level which
may
+ * not be processed, check isDebugEnabled() first.
+ */
+public interface TaggableLogger extends Logger {
+
+ TaggableLogger tag(String key, Object value);
+
+ // add tag method here
+
+ default TaggableLogger queryId(final TUniqueId queryId) {
+ return tag(TagKey.QUERY_ID, DebugUtil.printId(queryId));
+ }
+
+ default TaggableLogger queryId(final UUID queryId) {
+ return tag(TagKey.QUERY_ID, DebugUtil.printId(queryId));
+ }
+
+ default TaggableLogger queryId(final PUniqueId queryId) {
+ return tag(TagKey.QUERY_ID, DebugUtil.printId(queryId));
+ }
+
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/logger/TaggedLogFormat.java
b/fe/fe-core/src/main/java/org/apache/doris/common/logger/TaggedLogFormat.java
new file mode 100644
index 0000000..cf8fcf8
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/common/logger/TaggedLogFormat.java
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.common.logger;
+
+import org.apache.logging.log4j.message.Message;
+
+public interface TaggedLogFormat {
+
+ Message getTaggedMessage(Message message, Tags tags);
+
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/logger/Tags.java
b/fe/fe-core/src/main/java/org/apache/doris/common/logger/Tags.java
new file mode 100644
index 0000000..7485967
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/logger/Tags.java
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.common.logger;
+
+class Tags {
+ String key;
+ Object value;
+ Tags next;
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]