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 <45881148+ccoffl...@users.noreply.github.com>
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: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to