Hi Hans,

Agree with all your comments, and one clarification in regards to Write() as below.

Naming TraceLogBuffer and TraceLogClient, I don't have the other names for better meaning as of now. Maybe LogTraceBuffer/LogTraceClient instead, so they can match to the file names?

Thanks,

Minh

On 19/06/18 23:03, Hans Nordeback wrote:
Hi Minh,

ack, review only. Some comments below marked with [HansN]. (Perhaps TraceLogBuffer and TraceLogClient are better names?)

/Thanks HansN


On 06/11/2018 11:16 PM, Minh Chau wrote:
This ticket support the tracing buffer per thread. If enabled,
each thread has it own buffer which the trace is written to.
The thread trace buffers will be flushed to file if an abnormal
end hits, i.e. LOG_ER is called.
The thread trace file format: "daemon_name"_"proc_id"_"thread_id"
is stored under /var/log/opensaf/. These file can be correlated
in timing order by a "sort" command or could be by another saflog
command.
Main changes in this ticket
. Rename LogTrace class to LogTraceClient
. Move Log_Writer from dtm to base
. Add LogTraceBuffer class, this class hold the trace buffer
. Add thread_local instace of LogTraceBuffer, so that the instance
   of LogTraceBuffer will be created/deleted whenever a new thread
   is created or destroyed.
. Add an instance of LogTraceClient for thread trace buffering.
---
  src/amf/amfd/amfd.conf                     |   5 ++
  src/base/Makefile.am                       |   4 +
  src/base/daemon.c                          |   3 +
  src/base/log_writer.cc                     | 135 +++++++++++++++++++++++++++++
  src/base/log_writer.h                      |  65 ++++++++++++++
  src/base/logtrace.cc                       |  77 +++++++++++++---
  src/base/logtrace.h                        |   6 ++
  src/base/logtrace_buffer.cc                |  87 +++++++++++++++++++
  src/base/logtrace_buffer.h                 |  52 +++++++++++
  src/base/logtrace_client.cc                |  91 ++++++++++++++-----
  src/base/logtrace_client.h                 |  50 ++++++++---
  src/dtm/Makefile.am                        |   3 -
  src/dtm/transport/log_server.h             |   2 +-
  src/dtm/transport/log_writer.cc            | 117 -------------------------
  src/dtm/transport/log_writer.h             |  64 --------------
  src/dtm/transport/tests/log_writer_test.cc |   2 +-
  src/mds/mds_log.cc                         |  18 ++--
  17 files changed, 537 insertions(+), 244 deletions(-)
  create mode 100644 src/base/log_writer.cc
  create mode 100644 src/base/log_writer.h
  create mode 100644 src/base/logtrace_buffer.cc
  create mode 100644 src/base/logtrace_buffer.h
  delete mode 100644 src/dtm/transport/log_writer.cc
  delete mode 100644 src/dtm/transport/log_writer.h

diff --git a/src/amf/amfd/amfd.conf b/src/amf/amfd/amfd.conf
index 9da3bec..5e20650 100644
--- a/src/amf/amfd/amfd.conf
+++ b/src/amf/amfd/amfd.conf
@@ -24,3 +24,8 @@ export AVSV_HB_PERIOD=10000000000
  # All logging will be recorded in a new node local log file $PKGLOGDIR/osaf.log.   # Uncomment the next line to enable this service to log to OpenSAF node local log file.
  # export OSAF_LOCAL_NODE_LOG=1
+
+# THREAD_TRACE_BUFFER variable enables the tracing, writes the trace
+# to thread based buffer in circular fashion. The trace buffers will
+# be flushed to file if an abnormal end hits, i.e. LOG_ER is called
+export THREAD_TRACE_BUFFER=1
diff --git a/src/base/Makefile.am b/src/base/Makefile.am
index 65f9219..a6c3178 100644
--- a/src/base/Makefile.am
+++ b/src/base/Makefile.am
@@ -51,6 +51,8 @@ lib_libopensaf_core_la_SOURCES += \
      src/base/log_message.cc \
      src/base/logtrace.cc \
      src/base/logtrace_client.cc \
+    src/base/logtrace_buffer.cc \
+    src/base/log_writer.cc \
      src/base/mutex.cc \
      src/base/ncs_main_pub.c \
      src/base/ncs_sprr.c \
@@ -101,6 +103,8 @@ noinst_HEADERS += \
      src/base/log_message.h \
      src/base/logtrace.h \
      src/base/logtrace_client.h \
+    src/base/logtrace_buffer.h \
+    src/base/log_writer.h \
      src/base/macros.h \
      src/base/mutex.h \
      src/base/ncs_edu.h \
diff --git a/src/base/daemon.c b/src/base/daemon.c
index 361dd8d..ad052bf 100644
--- a/src/base/daemon.c
+++ b/src/base/daemon.c
@@ -549,6 +549,9 @@ void daemon_exit(void)
      unlink(fifo_file);
      unlink(__pidfile);
  +    /* flush the logtrace */
+    logtrace_exit_daemon();
+
      _Exit(0);
  }
  diff --git a/src/base/log_writer.cc b/src/base/log_writer.cc
new file mode 100644
index 0000000..eb079d6
--- /dev/null
+++ b/src/base/log_writer.cc
@@ -0,0 +1,135 @@
+/*      -*- OpenSAF  -*-
+ *
+ * (C) Copyright 2016 The OpenSAF Foundation
+ * Copyright Ericsson AB 2017 - All Rights Reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999.
+ * The complete license can be accessed from the following location:
+ * http://opensource.org/licenses/lgpl-license.php
+ * See the Copying file included with the OpenSAF distribution for full
+ * licensing terms.
+ *
+ * Author(s): Ericsson AB
+ *
+ */
+
+#include "base/log_writer.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
[HansN] remove stdio.h, already included below
+#include <cerrno>
+#include <cstdio>
+#include <cstring>
+#include "base/getenv.h"
+#include "osaf/configmake.h"
+
+LogWriter::LogWriter(const std::string& log_name, size_t max_backups,
+                                                  size_t max_file_size)
+    : log_file_{base::GetEnv<std::string>("pkglogdir", PKGLOGDIR) + "/" +
+                log_name},
+      fd_{-1},
+      current_file_size_{0},
+      current_buffer_size_{0},
+      max_backups_{max_backups},
+      max_file_size_{max_file_size},
+      buffer_{new char[kBufferSize]} {}
+
+LogWriter::~LogWriter() {
+  Flush();
+  Close();
+  delete[] buffer_;
+}
+
+std::string LogWriter::log_file(size_t backup) const {
+  std::string file_name = log_file_;
+  if (backup != 0) {
+    file_name += std::string(".") + std::to_string(backup);
+  }
+  return file_name;
+}
+
+void LogWriter::Open() {
+  if (fd_ < 0) {
+    int fd;
+    do {
+      fd = open(log_file(0).c_str(), O_WRONLY | O_CLOEXEC | O_CREAT,
+                S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+    } while (fd == -1 && errno == EINTR);
+    if (fd >= 0) {
+      off_t seek_result = lseek(fd, 0, SEEK_END);
+      if (seek_result >= 0) current_file_size_ = seek_result;
+      fd_ = fd;
+    }
+  }
+}
+
+void LogWriter::Close() {
+  int fd = fd_;
+  if (fd >= 0) {
+    close(fd);
+    fd_ = -1;
+    current_file_size_ = 0;
+  }
+}
+
+void LogWriter::RotateLog() {
+  Close();
+  unlink(log_file(max_backups_).c_str());
+  for (size_t i = max_backups_; i != 0; --i) {
+    std::string backup_name = log_file(i);
+    std::string previous_backup = log_file(i - 1);
+    if (rename(previous_backup.c_str(), backup_name.c_str()) != 0) {
+      unlink(previous_backup.c_str());
+    }
+  }
+}
+
+void LogWriter::Write(const char* bytes, size_t size) {
+  size_t bytes_written = 0;
+  size_t bytes_chunk = 0;
+  while (bytes_written < size) {
+    if ((size - bytes_written) > (kBufferSize - current_buffer_size_)) {
+      bytes_chunk = kBufferSize - current_buffer_size_;
+    } else {
+      bytes_chunk = size - bytes_written;
+    }
+    memcpy(current_buffer_position(), bytes + bytes_written, bytes_chunk);
+    bytes_written += bytes_chunk;
+    current_buffer_size_ += bytes_chunk;
[HansN] shouldn't it be kBufferSize - kMaxMessageSize || ?
+    if (current_buffer_size_ >= kBufferSize ||
[Minh]: It should be (current_buffer_size >= kBufferSize). The kMaxMessageSize is used in the other Write() because it wants to make sure there's enough space (kMaxMessageSize bytes at least) for the next Write() (ref LogServer::Run()). This new Write() does not use kMaxMessageSize, it copies @bytes to buffer_, and if buffer_ is full then writes the buffer_ to file, until all bytes of @bytes are copied.
[HansN] shouldn't it be, current_file_size_ > max_file_size_) Flush(); ? The same also in Write below ?
+        current_buffer_size_ >= max_file_size_) Flush();
[Minh]: It should be (current_buffer_size_ >= max_file_size), so we don't miss to flush if the max size is just reached.
+  }
+}
+
+void LogWriter::Write(size_t size) {
+  current_buffer_size_ += size;
+  if (current_buffer_size_ > kBufferSize - kMaxMessageSize ||
+      current_buffer_size_ >= max_file_size_) Flush();
+}
+
+void LogWriter::Flush() {
+  size_t size = current_buffer_size_;
+  current_buffer_size_ = 0;
+  if (size == 0) return;
+  if (fd_ < 0) Open();
+  if (fd_ < 0) return;
+  if (current_file_size_ >= max_file_size_) {
+    RotateLog();
+    if (fd_ < 0) Open();
+    if (fd_ < 0) return;
+  }
+  size_t bytes_written = 0;
+  while (bytes_written < size) {
+    ssize_t result = write(fd_, buffer_ + bytes_written, size - bytes_written);
+    if (result < 0) {
+      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) continue;
+      break;
+    }
+    bytes_written += result;
+  }
+  current_file_size_ += bytes_written;
+}
diff --git a/src/base/log_writer.h b/src/base/log_writer.h
new file mode 100644
index 0000000..9fddd52
--- /dev/null
+++ b/src/base/log_writer.h
@@ -0,0 +1,65 @@
+/*      -*- OpenSAF  -*-
+ *
+ * (C) Copyright 2016 The OpenSAF Foundation
+ * Copyright Ericsson AB 2017 - All Rights Reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999.
+ * The complete license can be accessed from the following location:
+ * http://opensource.org/licenses/lgpl-license.php
+ * See the Copying file included with the OpenSAF distribution for full
+ * licensing terms.
+ *
+ * Author(s): Ericsson AB
+ *
+ */
+
+#ifndef BASE_LOG_WRITER_H_
+#define BASE_LOG_WRITER_H_
+
+#include <cstddef>
+#include <string>
+#include "base/macros.h"
+
+// This class is responsible for writing MDS log messages to disk, and rotating
+// the log file when it exceeds the maximum file size limit.
+class LogWriter {
+ public:
+  constexpr static const size_t kMaxMessageSize = 2 * size_t{1024};
+
+  LogWriter(const std::string& log_name, size_t max_backups,
+                                          size_t max_file_size);
+  virtual ~LogWriter();
+
+  char* current_buffer_position() { return buffer_ + current_buffer_size_; }
+  bool empty() const { return current_buffer_size_ == 0; }
+
+  // Write @a size bytes of log message data in the memory pointed to by @a +  // buffer to the MDS log file. After the log message has been written, the +  // file will be rotated if necessary. This method performs blocking file I/O.
+  void Write(size_t size);
+  void Write(const char* bytes, size_t size);
+  void Flush();
+
+ private:
+  constexpr static const size_t kBufferSize = 128 * size_t{1024};
+  void Open();
+  void Close();
+  void RotateLog();
+
+  std::string log_file(size_t backup) const;
+
+  const std::string log_file_;
+  int fd_;
+  size_t current_file_size_;
+  size_t current_buffer_size_;
+  size_t max_backups_;
+  size_t max_file_size_;
+  char* buffer_;
+
+  DELETE_COPY_AND_MOVE_OPERATORS(LogWriter);
+};
+
+#endif  // BASE_LOG_WRITER_H_
diff --git a/src/base/logtrace.cc b/src/base/logtrace.cc
index b54f14e..3dff86b 100644
--- a/src/base/logtrace.cc
+++ b/src/base/logtrace.cc
@@ -29,6 +29,7 @@
  #include <unistd.h>
  #include "base/getenv.h"
  #include "base/logtrace_client.h"
+#include "base/logtrace_buffer.h"
  #include "base/ncsgl_defs.h"
    namespace global {
@@ -41,13 +42,21 @@ char *msg_id;
  int logmask;
  const char* osaf_log_file = "osaf.log";
  bool enable_osaf_log = false;
+bool enable_thread_trace_buffer = false;
    }  // namespace global
  -TraceLog* gl_trace = nullptr;
-TraceLog* gl_osaflog = nullptr;
+// legacy tracing, trace is written by osaftransportd
+LogTraceClient* gl_remote_trace = nullptr;
+// direct osaf services logging to a configured file
+LogTraceClient* gl_remote_osaflog = nullptr;
+// local thread trace buffering
+LogTraceClient* gl_local_thread_trace = nullptr;
+
  std::once_flag init_flag;
[HansN] instead of 10240 perhaps use a constant, kBufferSize_10MB
+thread_local LogTraceBuffer gl_thread_buffer{gl_local_thread_trace, 10240};
+
  static pid_t gettid() { return syscall(SYS_gettid); }
    /**
@@ -84,14 +93,28 @@ static void sighup_handler(int sig) {
  void trace_output(const char *file, unsigned line, unsigned priority,
                       unsigned category, const char *format, va_list ap) {
    char preamble[288];
+  const char* entry = nullptr;
      assert(priority <= LOG_DEBUG && category < CAT_MAX);
      if (strncmp(file, "src/", 4) == 0) file += 4;
    snprintf(preamble, sizeof(preamble), "%d:%s:%u %s %s", gettid(), file, line,
             global::prefix_name[priority + category], format);
-  TraceLog::Log(gl_trace, static_cast<base::LogMessage::Severity>(priority),
-      preamble, ap);
+  // legacy trace
+  if (is_logtrace_enabled(category)) {
+    entry = LogTraceClient::Log(gl_remote_trace,
+ static_cast<base::LogMessage::Severity>(priority), preamble, ap);
+  }
+  // thread trace
+  if (global::enable_thread_trace_buffer == true) {
+    // reuse @entry if legacy trace is enabled
+    if (!entry) {
+      entry = gl_local_thread_trace->CreateLogEntry(
+ static_cast<base::LogMessage::Severity>(priority),
+          preamble, ap);
+    }
+    gl_thread_buffer.WriteToBuffer(entry);
+  }
  }
    void log_output(const char *file, unsigned line, unsigned priority,
@@ -103,8 +126,15 @@ void log_output(const char *file, unsigned line, unsigned priority,
    if (strncmp(file, "src/", 4) == 0) file += 4;
    snprintf(preamble, sizeof(preamble), "%d:%s:%u %s %s", gettid(), file, line,
             global::prefix_name[priority + category], format);
-  TraceLog::Log(gl_osaflog, static_cast<base::LogMessage::Severity>(priority),
-      preamble, ap);
+  LogTraceClient::Log(gl_remote_osaflog,
+      static_cast<base::LogMessage::Severity>(priority), preamble, ap);
+  // Flush the thread buffer for logging error or lower
+  if (global::enable_thread_trace_buffer == true && gl_local_thread_trace &&
+      static_cast<base::LogMessage::Severity>(priority) <=
+      base::LogMessage::Severity::kErr) {
+    gl_thread_buffer.RequestFlush();
+    gl_thread_buffer.FlushBuffer();
+  }
  }
    void logtrace_log(const char *file, unsigned line, int priority,
@@ -154,7 +184,8 @@ void logtrace_trace(const char *file, unsigned line, unsigned category,
                      const char *format, ...) {
    va_list ap;
  -  if (is_logtrace_enabled(category) == false) return;
+  if (is_logtrace_enabled(category) == false &&
+      global::enable_thread_trace_buffer == false) return;
      va_start(ap, format);
    trace_output(file, line, LOG_DEBUG, category, format, ap);
@@ -177,14 +208,27 @@ static void logtrace_init_interal(const char *pathname, unsigned mask,
      result = global::msg_id != nullptr;
  +  // Initialize various type of logging instances based on
+  // environment variables
    if (result && mask != 0) {
-    if (!gl_trace) gl_trace = new TraceLog();
-    result = gl_trace->Init(global::msg_id, TraceLog::kBlocking);
+    if (!gl_remote_trace) {
+      gl_remote_trace = new LogTraceClient(global::msg_id,
+          LogTraceClient::kRemoteBlocking);
+    }
+  }
+  if (base::GetEnv("THREAD_TRACE_BUFFER", uint32_t{0}) == 1) {
+    global::enable_thread_trace_buffer = true;
+    if (!gl_local_thread_trace) {
+      gl_local_thread_trace = new LogTraceClient(global::msg_id,
+          LogTraceClient::kLocalBuffer);
+    }
    }
    if (base::GetEnv("OSAF_LOCAL_NODE_LOG", uint32_t{0}) == 1) {
      global::enable_osaf_log = true;
-    if (!gl_osaflog) gl_osaflog = new TraceLog();
-    result = gl_osaflog->Init(global::osaf_log_file, TraceLog::kBlocking);
+    if (!gl_remote_osaflog) {
+      gl_remote_osaflog = new LogTraceClient(global::osaf_log_file,
+          LogTraceClient::kRemoteBlocking);
+    }
    }
    if (result) {
      syslog(LOG_INFO, "logtrace: trace enabled to file '%s', mask=0x%x", @@ -204,6 +248,11 @@ int logtrace_init(const char *, const char *pathname, unsigned mask) {
    return result;
  }
  +int logtrace_exit_daemon() {
+  if (gl_local_thread_trace) gl_local_thread_trace->FlushExternalBuffer();
+  return 0;
+}
+
  int logtrace_init_daemon(const char *ident, const char *pathname,
                           unsigned tracemask, int logmask) {
    if (signal(SIGUSR2, sigusr2_handler) == SIG_ERR) {
@@ -231,8 +280,10 @@ int trace_category_set(unsigned mask) {
    if (global::category_mask == 0) {
      syslog(LOG_INFO, "logtrace: trace disabled");
    } else {
-    if (!gl_trace) gl_trace = new TraceLog();
-    gl_trace->Init(global::msg_id, TraceLog::kBlocking);
+    if (!gl_remote_trace) {
+      gl_remote_trace = new LogTraceClient(global::msg_id,
+        LogTraceClient::kRemoteBlocking);
+    }
      syslog(LOG_INFO, "logtrace: trace enabled to file %s, mask=0x%x",
             global::msg_id, global::category_mask);
    }
diff --git a/src/base/logtrace.h b/src/base/logtrace.h
index 9b20da8..5d2d667 100644
--- a/src/base/logtrace.h
+++ b/src/base/logtrace.h
@@ -96,6 +96,12 @@ extern int logtrace_init(const char *ident, const char *pathname,   extern int logtrace_init_daemon(const char *ident, const char *pathname,
                                  unsigned tracemask, int logmask);
  +/*
+ * logtrace_exit_daemon
+ * This should be called when a daemon exit
+ */
+extern int logtrace_exit_daemon();
+
  /**
   * trace_category_set - Set the mask used for trace filtering.
   *
diff --git a/src/base/logtrace_buffer.cc b/src/base/logtrace_buffer.cc
new file mode 100644
index 0000000..59099b2
--- /dev/null
+++ b/src/base/logtrace_buffer.cc
@@ -0,0 +1,87 @@
+/*      -*- OpenSAF  -*-
+ *
+ * Copyright Ericsson AB 2018 - All Rights Reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999.
+ * The complete license can be accessed from the following location:
+ * http://opensource.org/licenses/lgpl-license.php
+ * See the Copying file included with the OpenSAF distribution for full
+ * licensing terms.
+ *
+ */
+
+#include "base/logtrace_buffer.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <cerrno>
+#include <cstdio>
+#include <cstdint>
+#include <cstdlib>
+#include <thread>
+
+LogTraceBuffer::LogTraceBuffer(LogTraceClient* owner, size_t buffer_size) :
+  owner_(owner),
+  buffer_size_(buffer_size) {
+  std::string log_file_name;
[HansN] move all member variables to the constructor initializer list, e.g. index_, tid_, etc.
+  index_ = 0;
+  flush_required_ = false;
[HansN] consider a utility function pid_t gettid() instead of syscall directly
+  tid_ = syscall(SYS_gettid);
+  vector_.resize(buffer_size_);
+  if (owner_) {
+    owner_->AddExternalBuffer(tid_, this);
+    log_file_name = std::string(owner_->app_name()) + "_" + owner_->proc_id()
+        + "_" + std::to_string(tid_);
+  } else {
+    log_file_name = std::string(getpid() + "_" + std::to_string(tid_));
+  }
[HansN] use named constants instead of numbers
+  log_writer_ = new LogWriter{log_file_name, 10, 10 * 1024 * 1024};
+}
+
+LogTraceBuffer::~LogTraceBuffer() {
+  if (owner_) {
+    owner_->RemoveExternalBuffer(tid_);
+  }
+  FlushBuffer();
+  delete log_writer_;
+}
+
+void LogTraceBuffer::WriteToBuffer(std::string trace) {
+  vector_[index_] = trace;
+  // add break line char
+  if (trace.at(trace.length() - 1) != '\n')
+    vector_[index_] += "\n";
+
+  if (index_++ == buffer_size_) index_ = 0;
+  if (flush_required_) FlushBuffer();
+}
+void LogTraceBuffer::RequestFlush() {
+  flush_required_ = true;
+  if (owner_) owner_->RequestFlushExternalBuffer();
+}
+
+bool LogTraceBuffer::FlushBuffer() {
+  size_t i;
+  // flushing the right half first
+  for (i = index_ ; i < buffer_size_ ; i++) {
+    if (vector_[i].length() > 0) {
+      log_writer_->Write(vector_[i].c_str(), vector_[i].length());
+      vector_[i] = "";
+    }
+  }
+  // flushing the left half second
+  for (i = 0 ; i < index_ ; i++) {
+    if (vector_[i].length() > 0) {
+      log_writer_->Write(vector_[i].c_str(), vector_[i].length());
+      vector_[i] = "";
+    }
+  }
+  log_writer_->Flush();
+  flush_required_ = false;
+  return true;
+}
+
diff --git a/src/base/logtrace_buffer.h b/src/base/logtrace_buffer.h
new file mode 100644
index 0000000..535932a
--- /dev/null
+++ b/src/base/logtrace_buffer.h
@@ -0,0 +1,52 @@
+/*      -*- OpenSAF  -*-
+ *
+ * Copyright Ericsson AB 2018 - All Rights Reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999.
+ * The complete license can be accessed from the following location:
+ * http://opensource.org/licenses/lgpl-license.php
+ * See the Copying file included with the OpenSAF distribution for full
+ * licensing terms.
+ *
+ */
+
+#ifndef BASE_LOGTRACE_BUFFER_H_
+#define BASE_LOGTRACE_BUFFER_H_
+
+#include <vector>
+#include <string>
+#include <list>
+#include "base/buffer.h"
+#include "base/conf.h"
+#include "base/macros.h"
+#include "base/logtrace_client.h"
+#include "base/log_writer.h"
+
+class LogTraceClient;
+// A class implements a buffer which is written in circular fashion
+// This buffer is attached to its owner which is a LogTraceClient
+
+class LogTraceBuffer {
+ public:
+  explicit LogTraceBuffer(LogTraceClient* owner, size_t buffer_size);
+  ~LogTraceBuffer();
+  void WriteToBuffer(std::string trace);
+  bool FlushBuffer();
+  void RequestFlush();
+  void SetFlush(const bool flush) { flush_required_ = flush; }
+ private:
+  LogTraceClient* owner_;
+  const size_t buffer_size_;
+  size_t index_;
+  int64_t tid_;
+  std::vector<std::string> vector_;
+  LogWriter* log_writer_;
+  bool flush_required_;
+  DELETE_COPY_AND_MOVE_OPERATORS(LogTraceBuffer);
+};
+
+
+#endif  // BASE_LOGTRACE_BUFFER_H_
diff --git a/src/base/logtrace_client.cc b/src/base/logtrace_client.cc
index a9d82e2..e22112a 100644
--- a/src/base/logtrace_client.cc
+++ b/src/base/logtrace_client.cc
@@ -16,32 +16,35 @@
  #include "base/logtrace_client.h"
  #include <limits.h>
  #include <unistd.h>
-#include <cstring>
+#include <utility>
+#include <string>
  #include "base/getenv.h"
  #include "base/ncsgl_defs.h"
  #include "base/osaf_utility.h"
  #include "dtm/common/osaflog_protocol.h"
  -TraceLog::TraceLog()
+
+LogTraceClient::LogTraceClient(const char *msg_id, WriteMode mode)
      : sequence_id_{1}, buffer_{} {
-  mutex_ = nullptr;
+  log_mutex_ = nullptr;
+  ext_buffer_mutex_ = nullptr;
    log_socket_ = nullptr;
+  Init(msg_id, mode);
  }
  -TraceLog::~TraceLog() {
-  if (mutex_) delete mutex_;
+LogTraceClient::~LogTraceClient() {
+  if (log_mutex_) delete log_mutex_;
+  if (ext_buffer_mutex_) delete ext_buffer_mutex_;
    if (log_socket_) delete log_socket_;
  }
  -bool TraceLog::Init(const char *msg_id, WriteMode mode) {
+bool LogTraceClient::Init(const char *msg_id, WriteMode mode) {
    char app_name[NAME_MAX];
    char pid_path[PATH_MAX];
    uint32_t process_id;
    char *token, *saveptr;
    char *pid_name = nullptr;
  -  if (log_socket_ != nullptr && mutex_ != nullptr) return true;
-
    memset(app_name, 0, NAME_MAX);
    memset(pid_path, 0, PATH_MAX);
    process_id = getpid();
@@ -74,28 +77,46 @@ bool TraceLog::Init(const char *msg_id, WriteMode mode) {
    app_name_ = base::LogMessage::AppName(app_name);
    proc_id_ = base::LogMessage::ProcId{std::to_string(process_id)};
    msg_id_ = base::LogMessage::MsgId{msg_id};
-  log_socket_ = new base::UnixClientSocket{Osaflog::kServerSocketPath,
-    static_cast<base::UnixSocket::Mode>(mode)};
-  mutex_ = new base::Mutex{};
  +  if (mode == kRemoteBlocking || mode == kRemoteNonblocking) {
+    log_socket_ = new base::UnixClientSocket{Osaflog::kServerSocketPath,
+      static_cast<base::UnixSocket::Mode>(mode)};
+  }
+  log_mutex_ = new base::Mutex{};
+  ext_buffer_mutex_ = new base::Mutex{};
    return true;
  }
  -void TraceLog::Log(TraceLog* tracelog, base::LogMessage::Severity severity,
-      const char *fmt, va_list ap) {
-  if (tracelog != nullptr) tracelog->Log(severity, fmt, ap);
+const char* LogTraceClient::Log(LogTraceClient* tracelog,
+    base::LogMessage::Severity severity, const char *fmt, va_list ap) {
+  if (tracelog != nullptr) return tracelog->Log(severity, fmt, ap);
+  return nullptr;
  }
  -void TraceLog::Log(base::LogMessage::Severity severity, const char *fmt,
-                   va_list ap) {
-  if (log_socket_ != nullptr && mutex_ != nullptr) {
-    LogInternal(severity, fmt, ap);
+const char* LogTraceClient::Log(base::LogMessage::Severity severity,
+    const char *fmt, va_list ap) {
+  if (log_socket_ != nullptr && log_mutex_ != nullptr) {
+    return LogInternal(severity, fmt, ap);
    }
+  return nullptr;
+}
+
+const char* LogTraceClient::LogInternal(base::LogMessage::Severity severity,
+    const char *fmt, va_list ap) {
+  base::Lock lock(*log_mutex_);
+  CreateLogEntryInternal(severity, fmt, ap);
+  log_socket_->Send(buffer_.data(), buffer_.size());
+  return buffer_.data();
  }
  -void TraceLog::LogInternal(base::LogMessage::Severity severity, const char *fmt,
-                           va_list ap) {
-  base::Lock lock(*mutex_);
+const char* LogTraceClient::CreateLogEntry(base::LogMessage::Severity severity,
+    const char *fmt, va_list ap) {
+  base::Lock lock(*log_mutex_);
+  return CreateLogEntryInternal(severity, fmt, ap);
+}
+
+const char* LogTraceClient::CreateLogEntryInternal(
+    base::LogMessage::Severity severity, const char *fmt, va_list ap) {
    uint32_t id = sequence_id_;
    sequence_id_ = id < kMaxSequenceId ? id + 1 : 1;
    buffer_.clear();
@@ -106,5 +127,31 @@ void TraceLog::LogInternal(base::LogMessage::Severity severity, const char *fmt,
{base::LogMessage::Parameter{base::LogMessage::SdName{"sequenceId"},
                                       std::to_string(id)}}}},
        fmt, ap, &buffer_);
-  log_socket_->Send(buffer_.data(), buffer_.size());
+  return buffer_.data();
+}
+
+void LogTraceClient::AddExternalBuffer(int64_t tid, LogTraceBuffer* buffer) {
+  base::Lock lock(*ext_buffer_mutex_);
+  ext_buffer_map_.insert(std::pair<int64_t, LogTraceBuffer*>(tid, buffer));
  }
+
+void LogTraceClient::RemoveExternalBuffer(int64_t tid) {
+  base::Lock lock(*ext_buffer_mutex_);
+  ext_buffer_map_.erase(tid);
+}
+
+void LogTraceClient::RequestFlushExternalBuffer() {
+  base::Lock lock(*ext_buffer_mutex_);
+  for (auto &buff : ext_buffer_map_) {
+    buff.second->SetFlush(true);
+  }
+}
+
+bool LogTraceClient::FlushExternalBuffer() {
+  base::Lock lock(*ext_buffer_mutex_);
+  for (auto &buff : ext_buffer_map_) {
+    buff.second->FlushBuffer();
+  }
+  return true;
+}
+
diff --git a/src/base/logtrace_client.h b/src/base/logtrace_client.h
index 21c7d2e..5b165e5 100644
--- a/src/base/logtrace_client.h
+++ b/src/base/logtrace_client.h
@@ -16,32 +16,50 @@
  #ifndef BASE_LOGTRACE_CLIENT_H_
  #define BASE_LOGTRACE_CLIENT_H_
  +#include <map>
  #include "base/log_message.h"
-#include "base/unix_client_socket.h"
  #include "base/buffer.h"
  #include "base/conf.h"
+#include "base/logtrace_buffer.h"
  #include "base/macros.h"
  #include "base/mutex.h"
+#include "base/unix_client_socket.h"
    // A class implements trace/log client that communicates to the log server
  // running in osaftransportd.
-class TraceLog {
+class LogTraceBuffer;
+
+class LogTraceClient {
   public:
    enum WriteMode {
-    kBlocking = base::UnixSocket::Mode::kBlocking,
-    kNonblocking = base::UnixSocket::Mode::kNonblocking,
+    kRemoteBlocking = base::UnixSocket::Mode::kBlocking,
+    kRemoteNonblocking = base::UnixSocket::Mode::kNonblocking,
+    kLocalBuffer
    };
-  bool Init(const char *msg_id, WriteMode mode);
-  static void Log(TraceLog* tracelog, base::LogMessage::Severity severity,
-      const char *fmt, va_list ap);
-  void Log(base::LogMessage::Severity severity, const char *fmt,
+  LogTraceClient(const char *msg_id, WriteMode mode);
+  ~LogTraceClient();
+
+  static const char* Log(LogTraceClient* tracelog,
+      base::LogMessage::Severity severity, const char *fmt, va_list ap);
+  const char* Log(base::LogMessage::Severity severity, const char *fmt,
                    va_list ap);
-  TraceLog();
-  ~TraceLog();
+  const char* CreateLogEntry(base::LogMessage::Severity severity,
+      const char *fmt, va_list ap);
+  void AddExternalBuffer(int64_t tid, LogTraceBuffer* buffer);
+  void RemoveExternalBuffer(int64_t tid);
+  void RequestFlushExternalBuffer();
+
+  const char* app_name() const { return app_name_.data(); }
+  const char* proc_id() const { return proc_id_.data(); }
+
+  bool FlushExternalBuffer();
     private:
-  void LogInternal(base::LogMessage::Severity severity, const char *fmt,
-                     va_list ap);
+  bool Init(const char *msg_id, WriteMode mode);
+  const char* LogInternal(base::LogMessage::Severity severity, const char *fmt,
+      va_list ap);
+  const char* CreateLogEntryInternal(base::LogMessage::Severity severity,
+      const char *fmt, va_list ap);
    static constexpr const uint32_t kMaxSequenceId = uint32_t{0x7fffffff};
    base::LogMessage::HostName fqdn_{""};
    base::LogMessage::AppName app_name_{""};
@@ -50,8 +68,12 @@ class TraceLog {
    uint32_t sequence_id_;
    base::UnixClientSocket* log_socket_;
    base::Buffer<512> buffer_;
-  base::Mutex* mutex_;
-  DELETE_COPY_AND_MOVE_OPERATORS(TraceLog);
+  base::Mutex* log_mutex_;
+
+  std::map<int64_t, LogTraceBuffer*> ext_buffer_map_;
+  base::Mutex* ext_buffer_mutex_;
+
+  DELETE_COPY_AND_MOVE_OPERATORS(LogTraceClient);
  };
    #endif  // BASE_LOGTRACE_CLIENT_H_
diff --git a/src/dtm/Makefile.am b/src/dtm/Makefile.am
index f925e51..847f6d6 100644
--- a/src/dtm/Makefile.am
+++ b/src/dtm/Makefile.am
@@ -31,7 +31,6 @@ noinst_HEADERS += \
      src/dtm/dtmnd/dtm_node.h \
      src/dtm/dtmnd/multicast.h \
      src/dtm/transport/log_server.h \
-    src/dtm/transport/log_writer.h \
      src/dtm/transport/tests/mock_logtrace.h \
      src/dtm/transport/tests/mock_osaf_poll.h \
      src/dtm/transport/transport_monitor.h
@@ -67,7 +66,6 @@ bin_osaftransportd_CPPFLAGS = \
    bin_osaftransportd_SOURCES = \
      src/dtm/transport/log_server.cc \
-    src/dtm/transport/log_writer.cc \
      src/dtm/transport/main.cc \
      src/dtm/transport/transport_monitor.cc
  @@ -107,7 +105,6 @@ bin_transport_test_CPPFLAGS = \
  bin_transport_test_LDFLAGS = \
      $(AM_LDFLAGS) \
      src/base/lib_libopensaf_core_la-getenv.lo \
-    src/dtm/transport/bin_osaftransportd-log_writer.o \
      src/dtm/transport/bin_osaftransportd-transport_monitor.o
    bin_transport_test_SOURCES = \
diff --git a/src/dtm/transport/log_server.h b/src/dtm/transport/log_server.h
index 793465d..bbc3af8 100644
--- a/src/dtm/transport/log_server.h
+++ b/src/dtm/transport/log_server.h
@@ -26,7 +26,7 @@
  #include "base/macros.h"
  #include "base/unix_server_socket.h"
  #include "dtm/common/osaflog_protocol.h"
-#include "dtm/transport/log_writer.h"
+#include "base/log_writer.h"
    // This class implements a loop that receives log messages over a UNIX socket
  // and sends them to a LogWriter instance.
diff --git a/src/dtm/transport/log_writer.cc b/src/dtm/transport/log_writer.cc
deleted file mode 100644
index 0195c25..0000000
--- a/src/dtm/transport/log_writer.cc
+++ /dev/null
@@ -1,117 +0,0 @@
-/*      -*- OpenSAF  -*-
- *
- * (C) Copyright 2016 The OpenSAF Foundation
- * Copyright Ericsson AB 2017 - All Rights Reserved.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed - * under the GNU Lesser General Public License Version 2.1, February 1999.
- * The complete license can be accessed from the following location:
- * http://opensource.org/licenses/lgpl-license.php
- * See the Copying file included with the OpenSAF distribution for full
- * licensing terms.
- *
- * Author(s): Ericsson AB
- *
- */
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <cerrno>
-#include <cstdio>
-#include "base/getenv.h"
-#include "osaf/configmake.h"
-#include "dtm/transport/log_writer.h"
-#include "dtm/transport/log_server.h"
-
-LogWriter::LogWriter(const std::string& log_name, size_t max_backups,
-                                                  size_t max_file_size)
-    : log_file_{base::GetEnv<std::string>("pkglogdir", PKGLOGDIR) + "/" +
-                log_name},
-      fd_{-1},
-      current_file_size_{0},
-      current_buffer_size_{0},
-      max_backups_{max_backups},
-      max_file_size_{max_file_size},
-      buffer_{new char[kBufferSize]} {}
-
-LogWriter::~LogWriter() {
-  Flush();
-  Close();
-  delete[] buffer_;
-}
-
-std::string LogWriter::log_file(size_t backup) const {
-  std::string file_name = log_file_;
-  if (backup != 0) {
-    file_name += std::string(".") + std::to_string(backup);
-  }
-  return file_name;
-}
-
-void LogWriter::Open() {
-  if (fd_ < 0) {
-    int fd;
-    do {
-      fd = open(log_file(0).c_str(), O_WRONLY | O_CLOEXEC | O_CREAT,
-                S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
-    } while (fd == -1 && errno == EINTR);
-    if (fd >= 0) {
-      off_t seek_result = lseek(fd, 0, SEEK_END);
-      if (seek_result >= 0) current_file_size_ = seek_result;
-      fd_ = fd;
-    }
-  }
-}
-
-void LogWriter::Close() {
-  int fd = fd_;
-  if (fd >= 0) {
-    close(fd);
-    fd_ = -1;
-    current_file_size_ = 0;
-  }
-}
-
-void LogWriter::RotateLog() {
-  Close();
-  unlink(log_file(max_backups_).c_str());
-  for (size_t i = max_backups_; i != 0; --i) {
-    std::string backup_name = log_file(i);
-    std::string previous_backup = log_file(i - 1);
-    if (rename(previous_backup.c_str(), backup_name.c_str()) != 0) {
-      unlink(previous_backup.c_str());
-    }
-  }
-}
-
-void LogWriter::Write(size_t size) {
-  current_buffer_size_ += size;
-  if (current_buffer_size_ > kBufferSize - kMaxMessageSize ||
-      current_buffer_size_ >= max_file_size_) Flush();
-}
-
-void LogWriter::Flush() {
-  size_t size = current_buffer_size_;
-  current_buffer_size_ = 0;
-  if (size == 0) return;
-  if (fd_ < 0) Open();
-  if (fd_ < 0) return;
-  if (current_file_size_ >= max_file_size_) {
-    RotateLog();
-    if (fd_ < 0) Open();
-    if (fd_ < 0) return;
-  }
-  size_t bytes_written = 0;
-  while (bytes_written < size) {
-    ssize_t result = write(fd_, buffer_ + bytes_written, size - bytes_written);
-    if (result < 0) {
-      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) continue;
-      break;
-    }
-    bytes_written += result;
-  }
-  current_file_size_ += bytes_written;
-}
diff --git a/src/dtm/transport/log_writer.h b/src/dtm/transport/log_writer.h
deleted file mode 100644
index e70f9c2..0000000
--- a/src/dtm/transport/log_writer.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*      -*- OpenSAF  -*-
- *
- * (C) Copyright 2016 The OpenSAF Foundation
- * Copyright Ericsson AB 2017 - All Rights Reserved.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed - * under the GNU Lesser General Public License Version 2.1, February 1999.
- * The complete license can be accessed from the following location:
- * http://opensource.org/licenses/lgpl-license.php
- * See the Copying file included with the OpenSAF distribution for full
- * licensing terms.
- *
- * Author(s): Ericsson AB
- *
- */
-
-#ifndef DTM_TRANSPORT_LOG_WRITER_H_
-#define DTM_TRANSPORT_LOG_WRITER_H_
-
-#include <cstddef>
-#include <string>
-#include "base/macros.h"
-
-// This class is responsible for writing MDS log messages to disk, and rotating
-// the log file when it exceeds the maximum file size limit.
-class LogWriter {
- public:
-  constexpr static const size_t kMaxMessageSize = 2 * size_t{1024};
-
-  LogWriter(const std::string& log_name, size_t max_backups,
-                                          size_t max_file_size);
-  virtual ~LogWriter();
-
-  char* current_buffer_position() { return buffer_ + current_buffer_size_; }
-  bool empty() const { return current_buffer_size_ == 0; }
-
-  // Write @a size bytes of log message data in the memory pointed to by @a -  // buffer to the MDS log file. After the log message has been written, the -  // file will be rotated if necessary. This method performs blocking file I/O.
-  void Write(size_t size);
-  void Flush();
-
- private:
-  constexpr static const size_t kBufferSize = 128 * size_t{1024};
-  void Open();
-  void Close();
-  void RotateLog();
-
-  std::string log_file(size_t backup) const;
-
-  const std::string log_file_;
-  int fd_;
-  size_t current_file_size_;
-  size_t current_buffer_size_;
-  size_t max_backups_;
-  size_t max_file_size_;
-  char* buffer_;
-
-  DELETE_COPY_AND_MOVE_OPERATORS(LogWriter);
-};
-
-#endif  // DTM_TRANSPORT_LOG_WRITER_H_
diff --git a/src/dtm/transport/tests/log_writer_test.cc b/src/dtm/transport/tests/log_writer_test.cc
index e96831e..93bf21c 100644
--- a/src/dtm/transport/tests/log_writer_test.cc
+++ b/src/dtm/transport/tests/log_writer_test.cc
@@ -17,7 +17,7 @@
  #include <cstring>
  #include <fstream>
  #include <string>
-#include "dtm/transport/log_writer.h"
+#include "base/log_writer.h"
  #include "gtest/gtest.h"
    class LogWriterTest : public ::testing::Test {
diff --git a/src/mds/mds_log.cc b/src/mds/mds_log.cc
index 24bc398..5350a22 100644
--- a/src/mds/mds_log.cc
+++ b/src/mds/mds_log.cc
@@ -29,7 +29,7 @@
  #include "mds/mds_papi.h"
    int gl_mds_log_level = 3;
-TraceLog* gl_mds_log = nullptr;
+LogTraceClient* gl_mds_log = nullptr;
/*******************************************************************************
   * Funtion Name   :    mds_log_init
@@ -40,9 +40,9 @@ TraceLog* gl_mds_log = nullptr;
   *
*******************************************************************************/
  uint32_t mds_log_init(const char *) {
-  if (!gl_mds_log) gl_mds_log = new TraceLog();
-  if (!gl_mds_log->Init("mds.log", TraceLog::kNonblocking)) {
-    return NCSCC_RC_FAILURE;
+  if (!gl_mds_log) {
+    gl_mds_log = new LogTraceClient("mds.log",
+        LogTraceClient::kRemoteNonblocking);
    }
    tzset();
    log_mds_notify("BEGIN MDS LOGGING| ARCHW=%x|64bit=%zu\n", MDS_SELF_ARCHWORD,
@@ -62,7 +62,7 @@ void log_mds_critical(const char *fmt, ...) {
    if (gl_mds_log_level < NCSMDS_LC_CRITICAL) return;
    va_list ap;
    va_start(ap, fmt);
-  TraceLog::Log(gl_mds_log, base::LogMessage::Severity::kCrit, fmt, ap); +  LogTraceClient::Log(gl_mds_log, base::LogMessage::Severity::kCrit, fmt, ap);
    va_end(ap);
  }
  @@ -78,7 +78,7 @@ void log_mds_err(const char *fmt, ...) {
    if (gl_mds_log_level < NCSMDS_LC_ERR) return;
    va_list ap;
    va_start(ap, fmt);
-  TraceLog::Log(gl_mds_log, base::LogMessage::Severity::kErr, fmt, ap);
+  LogTraceClient::Log(gl_mds_log, base::LogMessage::Severity::kErr, fmt, ap);
    va_end(ap);
  }
  @@ -94,7 +94,7 @@ void log_mds_notify(const char *fmt, ...) {
    if (gl_mds_log_level < NCSMDS_LC_NOTIFY) return;
    va_list ap;
    va_start(ap, fmt);
-  TraceLog::Log(gl_mds_log, base::LogMessage::Severity::kNotice, fmt, ap); +  LogTraceClient::Log(gl_mds_log, base::LogMessage::Severity::kNotice, fmt, ap);
    va_end(ap);
  }
  @@ -110,7 +110,7 @@ void log_mds_info(const char *fmt, ...) {
    if (gl_mds_log_level < NCSMDS_LC_INFO) return;
    va_list ap;
    va_start(ap, fmt);
-  TraceLog::Log(gl_mds_log, base::LogMessage::Severity::kInfo, fmt, ap); +  LogTraceClient::Log(gl_mds_log, base::LogMessage::Severity::kInfo, fmt, ap);
    va_end(ap);
  }
  @@ -127,6 +127,6 @@ void log_mds_dbg(const char *fmt, ...) {
    if (gl_mds_log_level < NCSMDS_LC_DBG) return;
    va_list ap;
    va_start(ap, fmt);
-  TraceLog::Log(gl_mds_log, base::LogMessage::Severity::kDebug, fmt, ap); +  LogTraceClient::Log(gl_mds_log, base::LogMessage::Severity::kDebug, fmt, ap);
    va_end(ap);
  }




------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Opensaf-devel mailing list
Opensaf-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/opensaf-devel

Reply via email to