src/log/Makefile.am               |   12 +-
 src/log/config/logsv_classes.xml  |    7 +-
 src/log/logd/lgs_common.h         |   81 +++++++
 src/log/logd/lgs_config.cc        |  173 ++++++++++++++-
 src/log/logd/lgs_config.h         |    3 +-
 src/log/logd/lgs_dest.cc          |  409 ++++++++++++++++++++++++++++++++++++++
 src/log/logd/lgs_dest.h           |  294 +++++++++++++++++++++++++++
 src/log/logd/lgs_evt.cc           |   32 ++
 src/log/logd/lgs_imm.cc           |  211 ++++++++++++++++---
 src/log/logd/lgs_main.cc          |   11 +-
 src/log/logd/lgs_mbcsv.cc         |  193 +++++++++++++----
 src/log/logd/lgs_mbcsv.h          |    1 +
 src/log/logd/lgs_mbcsv_v5.cc      |   11 +
 src/log/logd/lgs_mbcsv_v6.cc      |   70 ++++++-
 src/log/logd/lgs_mbcsv_v6.h       |   30 ++-
 src/log/logd/lgs_nildest.cc       |  153 ++++++++++++++
 src/log/logd/lgs_nildest.h        |   72 ++++++
 src/log/logd/lgs_stream.cc        |   62 +++++-
 src/log/logd/lgs_stream.h         |   16 +
 src/log/logd/lgs_unixsock_dest.cc |  343 +++++++++++++++++++++++++++++++
 src/log/logd/lgs_unixsock_dest.h  |  245 ++++++++++++++++++++++
 src/log/logd/lgs_util.cc          |   34 +++
 src/log/logd/lgs_util.h           |   11 +-
 23 files changed, 2359 insertions(+), 115 deletions(-)


Here are major info, detailed info will be added to PR doc soon.
1) Add attribute "saLogRecordDestination" to log stream.
2) Add Local socket destintion handler
3) Add NILDEST destination type
4) Integrate into first increment made by Lennart

diff --git a/src/log/Makefile.am b/src/log/Makefile.am
--- a/src/log/Makefile.am
+++ b/src/log/Makefile.am
@@ -75,7 +75,12 @@ noinst_HEADERS += \
        src/log/logd/lgs_mbcsv_v6.h \
        src/log/logd/lgs_recov.h \
        src/log/logd/lgs_stream.h \
-       src/log/logd/lgs_util.h
+       src/log/logd/lgs_util.h \
+       src/log/logd/lgs_dest.h \
+       src/log/logd/lgs_nildest.h \
+       src/log/logd/lgs_unixsock_dest.h \
+       src/log/logd/lgs_common.h
+
 
 bin_PROGRAMS += bin/saflogger
 osaf_execbin_PROGRAMS += bin/osaflogd
@@ -123,7 +128,10 @@ bin_osaflogd_SOURCES = \
        src/log/logd/lgs_mds.cc \
        src/log/logd/lgs_recov.cc \
        src/log/logd/lgs_stream.cc \
-       src/log/logd/lgs_util.cc
+       src/log/logd/lgs_util.cc \
+       src/log/logd/lgs_dest.cc \
+       src/log/logd/lgs_nildest.cc \
+       src/log/logd/lgs_unixsock_dest.cc
 
 bin_osaflogd_LDADD = \
        lib/libosaf_common.la \
diff --git a/src/log/config/logsv_classes.xml b/src/log/config/logsv_classes.xml
--- a/src/log/config/logsv_classes.xml
+++ b/src/log/config/logsv_classes.xml
@@ -147,12 +147,13 @@
                        <category>SA_CONFIG</category>
                        <flag>SA_WRITABLE</flag>
                </attr>
-               <attr>
+               <attr>
                        <name>saLogRecordDestination</name>
-                       <type>SA_UINT32_T</type>
+                       <type>SA_STRING_T</type>
                        <category>SA_CONFIG</category>
                        <flag>SA_WRITABLE</flag>
-                        <flag>SA_MULTI_VALUE</flag>
+                       <flag>SA_MULTI_VALUE</flag>
+                       <flag>SA_NO_DUPLICATES</flag>
                </attr>
                <attr>
                        <name>saLogStreamCreationTimestamp</name>
diff --git a/src/log/logd/lgs_common.h b/src/log/logd/lgs_common.h
new file mode 100644
--- /dev/null
+++ b/src/log/logd/lgs_common.h
@@ -0,0 +1,81 @@
+/*      -*- OpenSAF  -*-
+ *
+ * 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 SRC_LOG_LOGD_LGS_COMMON_H_
+#define SRC_LOG_LOGD_LGS_COMMON_H_
+
+#include <string>
+#include <vector>
+
+// Status of connection to destination
+enum DestinationStatus {
+  // Destination is configured and is being connected to
+  kActive = 0,
+  // Destination is configured but not able to connected to
+  // or not given any configuration value.
+  kFailed = 1,
+};
+
+// This error code is introduced in alternative destination ticket #2258.
+// and for now, only be used internally in lgs_dest files.
+enum ErrCode {
+  // No error happens
+  kOk = 0,
+  // Error happens in general
+  kErr,
+  // Passing invalid parameters
+  kInvalid,
+  // Not able to serve the request as the requester is being busy
+  kBusy,
+  // The request is timeout
+  kTimeOut,
+  // No memory
+  kNoMem,
+  // The request/msg is drop as the precondition is not met
+  kDrop
+};
+
+// Type of modification on multiple attribute values.
+// Introduce this own enum as (xvunguy) don't want to
+// have dependent to IMM header file just because we need IMM modification 
type.
+// Their meanings are equivelent to `SaImmAttrModificationTypeT`
+enum ModifyType { kAdd = 1, kDelete = 2, kReplace = 3 };
+
+//>
+// Define the "token" possition in destination configuration.
+// Each destination configiration has format "name;type;value".
+// After parsing, the output will put to the vector of strings
+// with format {"name", "type", "value"}.
+// kName : use this index to get "name"
+// kType : use this index to get "type"
+// kValue: use this index to get "value"
+// kSize : this is the maximum size of the dest configuration vector.
+//<
+enum { kName = 0, kType, kValue, kSize };
+
+//==============================================================================
+// Two interfaces are published to outside world
+//==============================================================================
+
+// Typedef for shorten declaration
+// This type could be used to hold set of strings with formats
+// 1) {"name;type;value", etc.}
+// 2) {"name", "type", "value"}
+// 3) etc.
+using VectorString = std::vector<std::string>;
+
+#endif  // SRC_LOG_LOGD_LGS_COMMON_H_
diff --git a/src/log/logd/lgs_config.cc b/src/log/logd/lgs_config.cc
--- a/src/log/logd/lgs_config.cc
+++ b/src/log/logd/lgs_config.cc
@@ -709,36 +709,168 @@ static int lgs_cfg_verify_log_filesys_co
   return rc;
 }
 
+//>
+// Utility functions to validate destination configuration format
+//<
+// Typedef for shorten declaration
+// This type could be used to hold set of strings with formats
+// 1) {"name;type;value", etc.}
+// 2) {"name", "type", "value"}
+// 3) etc.
+using VectorString = std::vector<std::string>;
+//>
+// Define the "token" possition in destination configuration.
+// kName : use this index to get "name" token
+// kType : use this index to get "type" token
+// kValue: use this index to get "value" token
+// kSize : this is the maximum size of the dest configuration vector.
+//<
+enum { kName = 0, kType, kValue, kSize };
+// Tokens seperator
+const char kSemicolon[] = ";";
+
+
+// The format of destination must be one of followings:
+// 1) "name;type;"
+// 2) "name;type;value"
+// So, in destination configuration, must have 02 semiconlons
+// no more, no less.
+//
+bool is_right_destination_fmt(const VectorString& vdest) {
+  int nl_cnt = 0;
+  // Check each single destination
+  for (const auto& it : vdest) {
+    nl_cnt = std::count(it.begin(), it.end(), ';');
+    if (nl_cnt != 2) {
+      TRACE("%s wrong destination format", __func__);
+      return false;
+    }
+  }
+  return true;
+}
+
+// Return false if "name" token is invalid
+bool is_name_valid(const VectorString& vdest) {
+  // Check each single destination
+  for (const auto& it : vdest) {
+    const VectorString sdes = logutil::Parser(it, kSemicolon);
+    if (sdes[kName].length() == 0) {
+      TRACE("%s wrong name format (name is empty value)", __func__);
+      return false;
+    }
+    // invalid name if having special characters in
+    if (logutil::isValidName(sdes[kName]) == false) {
+      TRACE("%s wrong name format (has special chars in)", __func__);
+      return false;
+    }
+
+    if (sdes[kName].length() > 255) {
+      TRACE("%s too long name", __func__);
+      return false;
+    }
+  }
+  return true;
+}
+
+// Return false if "type" token is invalid
+bool is_type_valid(const VectorString& vdest) {
+  // Check each single destination
+  for (const auto& it : vdest) {
+    const VectorString sdes = logutil::Parser(it, kSemicolon);
+    if (sdes[kType].length() == 0) {
+      TRACE("%s wrong type format (type is empty value)", __func__);
+      return false;
+    }
+    // Only support type = "UNIX_SOCKET"
+    if (sdes[kType] != "UNIX_SOCKET" && sdes[kType] != "NILDEST") {
+      TRACE("%s wrong type format. Only UNIX_SOCKET and NILDEST supported",
+            __func__);
+      return false;
+    }
+  }
+  return true;
+}
+
+// Return true if no dulicated info, otherwise false.
+bool is_no_config_duplicated(const VectorString& vdest,
+                                 const VectorString& vdes2) {
+  // Compare values of each pair of destinations.
+  for (const auto& it : vdest) {
+    const VectorString sdes = logutil::Parser(it, kSemicolon);
+    for (const auto& it2 : vdes2) {
+      if (it == it2) continue;
+      const VectorString sdes2 = logutil::Parser(it2, kSemicolon);
+      // Duplicate name
+      if (sdes2[kName] == sdes[kName]) {
+        TRACE("%s name is duplicated", __func__);
+        return false;
+      }
+      // Ignore nildest
+      if ((sdes[kValue].length() == 0) || (sdes2[kValue].length() == 0))
+        continue;
+      // Duplicate "value"
+      if (sdes2[kValue] == sdes[kValue]) {
+        TRACE("%s value is duplicated", __func__);
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+// Check whether there is duplicated "name" or "value"
+// in provided @vdest and existing destinations
+bool check_configuration_duplicated(const VectorString& vdest,
+                                    SaImmAttrModificationTypeT type) {
+  if (type == SA_IMM_ATTR_VALUES_DELETE) return true;
+  // No checking duplicated if replacing to one value
+  if (type == SA_IMM_ATTR_VALUES_REPLACE && vdest.size() < 2) return true;
+  // Check in all replace values if any duplicated name/value
+  if (type == SA_IMM_ATTR_VALUES_REPLACE)  {
+    // Check whethere there is duplicated "name" or "value"
+    // in list of provided destination configurations.
+    return is_no_config_duplicated(vdest, vdest);
+  } else {
+    bool isNoDuplicated = true;
+    // Check whethere there is duplicated "name" or "value"
+    // in adding destination configurations and existing ones.
+    // Firstly, check if any duplicate in added items.
+    if (vdest.size() > 1) {
+      isNoDuplicated = is_no_config_duplicated(vdest, vdest);
+      if (isNoDuplicated == false) return false;
+    }
+    // Secondly, check if any duplicate in added items with existing list.
+    return is_no_config_duplicated(
+        vdest, lgs_conf.logRecordDestinationConfiguration);
+  }
+}
+
 /**
  * Verify all values of log_record_destination_configuration
  * Rules:
  * - Empty string is Ok else
- * - String shall have at least three fields separated by '\n'
- * - First and second field cannot be empty
+ * - The destination configiration must be in right format
+ * - "name" token must be valid
+ * - "type" token must be valid
+ * - Must no duplicated info
  *
  * @param log_record_destination_configuration[in]
  * @return -1 on error
  */
 int lgs_cfg_verify_log_record_destination_configuration(
-  std::vector<std::string>& log_record_destination_configuration) {
-  int rc = 0;
-  TRACE_ENTER();
-
-  int nl_cnt = 0;
-  for (auto& config : log_record_destination_configuration) {
-    // Verify that the string contains at least 2 ';'
-    nl_cnt = std::count(config.begin(), config.end(), ';');
-    if (nl_cnt < 2) {
-      rc = -1;
-      break;
-    }
-  }
-
-  TRACE_LEAVE2("rc = %s", rc == -1? "Fail": "Pass");
-  return rc;
+    std::vector<std::string>& vdest,
+    SaImmAttrModificationTypeT type) {
+  // Allow deleting all destinations.
+  if (vdest.size() == 0) return true;
+  // It is important to keep the check in order
+  bool result = (is_right_destination_fmt(vdest) &&
+                 is_name_valid(vdest) &&
+                 is_type_valid(vdest) &&
+                 check_configuration_duplicated(vdest, type)
+                 );
+  return (result == true) ? (0) : (-1);
 }
 
-
 /**
  * Verify logRootDirectory; path to be used as log root directory
  * Rules:
@@ -846,7 +978,8 @@ static int verify_all_init() {
   }
 
   if (lgs_cfg_verify_log_record_destination_configuration(
-      lgs_conf.logRecordDestinationConfiguration) == -1) {
+          lgs_conf.logRecordDestinationConfiguration,
+          SA_IMM_ATTR_VALUES_ADD) == -1) {
     lgs_conf.logRecordDestinationConfiguration.clear();
     lgs_conf.logRecordDestinationConfiguration_cnfflag = LGS_CNF_DEF;
     rc = -1;
diff --git a/src/log/logd/lgs_config.h b/src/log/logd/lgs_config.h
--- a/src/log/logd/lgs_config.h
+++ b/src/log/logd/lgs_config.h
@@ -304,7 +304,8 @@ int lgs_cfg_verify_mbox_limit(uint32_t h
 int lgs_cfg_verify_max_application_streams(uint32_t max_app_streams);
 int lgs_cfg_verify_file_io_timeout(uint32_t log_file_io_timeout);
 int lgs_cfg_verify_log_record_destination_configuration(
-                std::vector<std::string>& 
log_record_destination_configuration);
+    std::vector<std::string>& vdest,
+    SaImmAttrModificationTypeT type);
 /*
  * Functions for updating some parameters. Used to support check-point before
  * version 5
diff --git a/src/log/logd/lgs_dest.cc b/src/log/logd/lgs_dest.cc
new file mode 100644
--- /dev/null
+++ b/src/log/logd/lgs_dest.cc
@@ -0,0 +1,409 @@
+/*      -*- OpenSAF  -*-
+ *
+ * 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 "log/logd/lgs_dest.h"
+
+#include <algorithm>
+
+#include "log/logd/lgs_util.h"
+#include "log/logd/lgs_config.h"
+#include "log/logd/lgs_nildest.h"
+#include "log/logd/lgs_unixsock_dest.h"
+#include "base/logtrace.h"
+#include "base/hash.h"
+
+static const char kDelimeter[] = ";";
+
+//==============================================================================
+// DestinationHandler class
+//==============================================================================
+DestinationHandler DestinationHandler::me_;
+
+// Only support "kUnix" and "kNilDest" so far
+DestinationHandler::DestType DestinationHandler::GetDestType(
+    const std::string& type) {
+  if (type == UnixSocketType::Type()) {
+    return DestType::kUnix;
+  } else if (type == NilDestType::Type()) {
+    return DestType::kNilDest;
+  } else {
+    LOG_NO("Unknown destination type %s", type.c_str());
+    return DestType::kUnknown;
+  }
+}
+
+std::string DestinationHandler::GenerateMsgId(
+    const std::string& dn, bool isRtStream) {
+  const std::string parent = ",safApp=safLogService";
+  std::string msgid{""};
+  size_t posa = 0, posb = 0;
+
+  // Extract stream name from stream DN.
+  posa = dn.find('=');
+  if ((posb = dn.find(',')) == std::string::npos) {
+    posb = dn.length();
+  }
+  std::string sname = dn.substr(posa + 1, posb - posa - 1);
+
+  //>
+  // Rules to generate msgid string:
+  // 1) Use stream name (e.g: saLogSystem) + 'C'/'R' if it is not over 31 chars
+  //    and the stream DN contains the @parent ",safApp=safLogService";
+  //    Note: 'C' means configuration stream, 'R' means runtime stream.
+  //           Why 31? It is due to MSGID field length limitation
+  //           (max is 32 chars length).
+  // 2) Otherwise, generate a hash number from stream DN
+  //    if stream name is over 31 chars.
+  //<
+  if (sname.length() < 32 && dn.find(parent) != std::string::npos) {
+    msgid = ((isRtStream == true) ? std::string{sname + 'R'} :
+             std::string{sname + 'C'});
+  } else {
+    msgid = base::Hash(dn);
+  }
+
+  return msgid;
+}
+
+DestinationHandler::DestType DestinationHandler::ToDestType(
+    const std::string& name) {
+  if (nametype_map_.find(name) == nametype_map_.end())
+    return DestType::kUnknown;
+  return nametype_map_[name];
+}
+
+void DestinationHandler::UpdateDestTypeDb(
+    const DestinationHandler::HandleMsg& msg) {
+  MsgType mtype = msg.type;
+
+  switch (mtype) {
+    case MsgType::kCfgDest: {
+      const CfgDestMsg& cdest = msg.info.cfg;
+      TRACE("%s kCfgDest (%s)", __func__, cdest.name);
+      nametype_map_[std::string{cdest.name}] =
+          GetDestType(std::string{cdest.type});
+      break;
+    }
+
+    case MsgType::kDelDest: {
+      const DelDestMsg& deldest = msg.info.del;
+      TRACE("%s kDelDest (%s)", __func__, deldest.name);
+      nametype_map_.erase(std::string{deldest.name});
+      break;
+    }
+
+    case MsgType::kNoDest: {
+      TRACE("%s kNoDest", __func__);
+      for (auto& it : nametype_map_) nametype_map_.erase(it.first);
+      break;
+    }
+
+    default:
+      LOG_WA("Unknown Dispatch message type (%d)", static_cast<int>(mtype));
+      break;
+  }
+}
+
+ErrCode DestinationHandler::DispatchTo(
+    const DestinationHandler::HandleMsg& msg,
+    const std::string& name) {
+  ErrCode rc = ErrCode::kOk;
+  MsgType mtype = msg.type;
+  // All destinations has been deleted. Don't care of destination types.
+  // Delete all existing connections of all types.
+  if (mtype == MsgType::kNoDest) {
+    NilDestType::Instance().ProcessMsg(msg);
+    rc = UnixSocketType::Instance().ProcessMsg(msg);
+    // Update the destination names from @DestTypeDb
+    UpdateDestTypeDb(msg);
+    UpdateRtDestStatus();
+    return rc;
+  }
+
+  // Update the @DestTypeDb first before processing the DestinationHandler msg.
+  // Otherwise, finding destination @type will result nothing.
+  // Then, the msg will be drop as no destination type found.
+  // But with @MsgType::kDelDest, updating should be last
+  // after fowarding the msg to @UnixSocketType.
+  // Otherwise, resource might be leaked (e.g: connection is not closed)
+  if (mtype == MsgType::kCfgDest) {
+    UpdateDestTypeDb(msg);
+  }
+
+  DestType destType = ToDestType(name);
+  switch (destType) {
+    // Pass the DestinationHandler msg to Local Unix Domain
+    // socket destination type for further processing.
+    case DestType::kUnix:
+      rc = UnixSocketType::Instance().ProcessMsg(msg);
+      break;
+
+    case DestType::kNilDest:
+      rc = NilDestType::Instance().ProcessMsg(msg);
+      break;
+
+    default:
+      LOG_ER("Not support this destination type(%d), or no dest configured!",
+             static_cast<int>(destType));
+      rc = ErrCode::kDrop;
+      break;
+  }
+
+  // Delete destination names from @DestTypeDb
+  if (mtype == MsgType::kDelDest) {
+    UpdateDestTypeDb(msg);
+  }
+
+  UpdateRtDestStatus();
+  return rc;
+}
+
+// Update the `logRecordDestinationStatus` here whenever
+// getting CfgDestination or WriteToDestination (?)
+void DestinationHandler::UpdateRtDestStatus() {
+  VectorString vstatus = GetDestinationStatus();
+  lgs_config_chg_t config_data = {NULL, 0};
+  lgs_cfgupd_mutival_replace(LOG_RECORD_DESTINATION_STATUS,
+                             vstatus, &config_data);
+  // Update configuration data store, no need to checkpoint the status to stb.
+  int ret = lgs_cfg_update(&config_data);
+  if (ret == -1) {
+    LOG_WA("%s lgs_cfg_update Fail", __func__);
+  }
+}
+
+void DestinationHandler::FormCfgDestMsg(
+    const std::string& dest, CfgDestMsg* msg) {
+  osafassert(msg != nullptr);
+  const VectorString tmp = logutil::Parser(dest, kDelimeter);
+  strncpy(msg->name, tmp[kName].c_str(), kMaxChar);
+  if (tmp.size() > 1) {
+    strncpy(msg->type, tmp[kType].c_str(), kMaxChar);
+  }
+  if (tmp.size() == kSize)
+    strncpy(msg->value, tmp[kValue].c_str(), kMaxChar);
+}
+
+void DestinationHandler::FormDelDestMsg(
+    const std::string& dest, DelDestMsg* msg) {
+  osafassert(msg != nullptr);
+  const VectorString tmp = logutil::Parser(dest, kDelimeter);
+  strncpy(msg->name, tmp[kName].c_str(), kMaxChar);
+}
+
+bool DestinationHandler::VectorFind(
+    const VectorString& vec, const std::string name) {
+  return std::find(vec.begin(), vec.end(), name) != vec.end();
+}
+
+ErrCode DestinationHandler::AddDestConfig(const VectorString& vdest) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  CfgDestMsg cmsg;
+  // Go thought all destinations and configure one by one
+  for (unsigned i = 0; i < vdest.size(); i++) {
+    HandleMsg msg{};
+    FormCfgDestMsg(vdest[i], &cmsg);
+    msg.type = MsgType::kCfgDest;
+    memcpy(&msg.info.cfg, &cmsg, sizeof(cmsg));
+    ret = DispatchTo(msg, cmsg.name);
+    allcfgdests_map_.push_back(vdest[i]);
+  }
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode DestinationHandler::DelDestConfig(const VectorString& vdest) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  DelDestMsg dmsg;
+  // Go thought all destinations and configure one by one
+  for (unsigned i = 0; i < vdest.size(); i++) {
+    if (VectorFind(allcfgdests_map_, vdest[i]) == false) {
+      LOG_NO("Not have such dest configuration (%s)", vdest[i].c_str());
+      ret = ErrCode::kDrop;
+      continue;
+    }
+
+    HandleMsg msg{};
+    FormDelDestMsg(vdest[i], &dmsg);
+    msg.type = MsgType::kDelDest;
+    memcpy(&msg.info.del, &dmsg, sizeof(dmsg));
+    ret = DispatchTo(msg, dmsg.name);
+    // Remove deleted destination from internal database
+    allcfgdests_map_.erase(std::remove(allcfgdests_map_.begin(),
+                                       allcfgdests_map_.end(), vdest[i]),
+                           allcfgdests_map_.end());
+  }
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode DestinationHandler::DelAllCfgDest() {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  // Don't care destination name
+  const std::string name{""};
+  HandleMsg msg{};
+  msg.type = MsgType::kNoDest;
+  ret = DispatchTo(msg, name);
+  allcfgdests_map_.clear();
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode DestinationHandler::ProcessCfgChange(
+    const VectorString& vdest, ModifyType type) {
+  ErrCode ret = ErrCode::kOk;
+  TRACE_ENTER();
+
+  // Case #1: perform all destination delete, but no existing at all.
+  if ((vdest.size() == 0) && (allcfgdests_map_.size() == 0)) {
+    return ErrCode::kOk;
+  }
+
+  // Case #2: Delete all existing destinations
+  if ((vdest.size() == 0) && (allcfgdests_map_.size() != 0)) {
+    return DelAllCfgDest();
+  }
+
+  // Case #3: Delete destination configurations while no dest cfg yet
+  if ((type == ModifyType::kDelete) &&
+      (vdest.size() != 0) &&
+      (allcfgdests_map_.size() == 0)) {
+    return ErrCode::kDrop;
+  }
+
+  // Case #4: Delete all destination configurations
+  if ((type == ModifyType::kDelete) &&
+      (vdest.size() == 0) &&
+      (allcfgdests_map_.size() != 0)) {
+    return DelAllCfgDest();
+  }
+
+  // Case #5: Delete all destinations but no one existing.
+  if ((type == ModifyType::kDelete) &&
+      (vdest.size() == 0) &&
+      (allcfgdests_map_.size() == 0)) {
+    return ErrCode::kDrop;
+  }
+
+  switch (type) {
+    case ModifyType::kAdd:
+      // Add new destinations to existing destinations.
+      ret = AddDestConfig(vdest);
+      break;
+
+    case ModifyType::kReplace:
+      // Step #1: Delete all existing destinations if any.
+      if (nametype_map_.size() != 0) {
+        DelAllCfgDest();
+      }
+      // Step #2: Create new destinations
+      ret = AddDestConfig(vdest);
+      break;
+
+    case ModifyType::kDelete:
+      ret = DelDestConfig(vdest);
+      break;
+
+    default:
+      LOG_ER("Unknown modify type (%d)", static_cast<int>(type));
+      ret = ErrCode::kErr;
+      break;
+  }
+
+  TRACE_LEAVE2("ret = %d", static_cast<int>(ret));
+  return ret;
+}
+
+ErrCode DestinationHandler::ProcessWriteReq(
+    const RecordInfo& info,
+    const std::string& name) {
+  HandleMsg msg{};
+  RecordMsg record;
+
+  msg.type = MsgType::kSendRecord;
+  strncpy(record.name, name.c_str(), kMaxChar);
+  memcpy(&record.rec, &info, sizeof(RecordInfo));
+  memcpy(&msg.info.rec, &record, sizeof(RecordMsg));
+  return DispatchTo(msg, name);
+}
+
+VectorString DestinationHandler::GetDestinationStatus() {
+  VectorString unixsock_type{};
+  VectorString nildest_type{};
+  // Go thought all 02 supported destination types
+  unixsock_type = UnixSocketType::Instance().GetAllDestStatus();
+  nildest_type = NilDestType::Instance().GetAllDestStatus();
+  unixsock_type.insert(unixsock_type.end(), nildest_type.begin(),
+                       nildest_type.end());
+
+  return unixsock_type;
+}
+
+//==============================================================================
+// Exposed interfaces to outer world
+//==============================================================================
+bool CfgDestination(const VectorString& vdest, ModifyType type) {
+  return (DestinationHandler::Instance().ProcessCfgChange(vdest, type) ==
+          ErrCode::kOk);
+}
+
+bool WriteToDestination(const RecordData& data,
+                        const VectorString& destnames) {
+  osafassert(data.name != nullptr && data.logrec != nullptr);
+  osafassert(data.hostname != nullptr && data.networkname != nullptr);
+
+  if (destnames.size() == 0) {
+    TRACE("%s No destination goes with this stream (%s)", __func__, data.name);
+    return true;
+  }
+
+  bool ret = true;
+  DestinationHandler::RecordInfo info;
+  std::string networkname = (std::string{data.networkname}.length() > 0) ?
+                            ("." + std::string {data.networkname}) : "";
+
+  // Origin is FQDN = <hostname>[.<networkname>]
+  // hostname = where the log record comes from (not active node)
+  const std::string origin = std::string {data.hostname} + networkname;
+  const std::string msgid = DestinationHandler::Instance().GenerateMsgId(
+      data.name, data.isRtStream);
+
+  info.msgid      = msgid.c_str();
+  info.log_record = data.logrec;
+  info.record_id  = data.recordId;
+  info.stream_dn  = data.name;
+  info.app_name   = data.appname;
+  info.severity   = data.sev;
+  info.time       = data.time;
+  info.origin     = origin.c_str();
+
+  // Go thought all destination names and send to the destination
+  // that go with that destination name
+  for (const auto& destname : destnames) {
+    ret = (DestinationHandler::Instance().ProcessWriteReq(info, destname)
+           == ErrCode::kOk);
+  }
+
+  return ret;
+}
+
+VectorString GetDestinationStatus() {
+  return DestinationHandler::Instance().GetDestinationStatus();
+}
diff --git a/src/log/logd/lgs_dest.h b/src/log/logd/lgs_dest.h
new file mode 100644
--- /dev/null
+++ b/src/log/logd/lgs_dest.h
@@ -0,0 +1,294 @@
+/*      -*- OpenSAF  -*-
+ *
+ * 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 SRC_LOG_LOGD_LGS_DEST_H_
+#define SRC_LOG_LOGD_LGS_DEST_H_
+
+#include <string>
+#include <map>
+
+#include "log/logd/lgs_common.h"
+#include "base/time.h"
+
+//>
+// @CfgDestination - Configure Destinations.
+// @input vdest: a vector of destination configurations.
+// @input type : modification type on destination configuration attribute.
+//               It has equivalent value to @SaImmAttrModificationTypeT.
+// @return: true if everything is OK, otherwise false.
+//
+// Call this function in CCB modified apply callback context,
+// when changes on configuration destination has been verified.
+// @type could be ModifyType::kAdd, ModifyType::kReplace, or 
ModifyType::kDelete
+//
+// E.g:
+// 1) Case #1: create 02 new destinations (@type = ModifyType::kAdd)
+//    Then, @vdest should contain data {"a;b;c", "d;e;f"}
+// 2) Case #2: modify one destinations. E.g: dest name @d -> @d1
+//    Then, @type = ModifyType::kReplace and @vdest contains data {"d1;e;f"}
+// 3) Case #3: delete one destination. Eg: dest name @a
+//    Then, @type = ModifyType::kDelete and @vdest should contains {"a;b;c"}
+// 4) Case #4: delete all destinations
+//    Then, @type = ModifyType::kDelete and @vdest contains empty vector {}
+//
+// The function will parse the provided data @vdest, getting
+// destination @name, destination @type, @destination value if any.
+// Then form the right handle msg, and forward it to right destination.
+//
+// This function is blocking. The caller will be blocked until
+// the request is done.
+//<
+bool CfgDestination(const VectorString& vdest, ModifyType type);
+
+//>
+// Carry necessary information to form RFC5424 syslog message.
+// Aim to reduce number of parameter passing to @WriteToDestination
+// @name       : log stream distinguish name
+// @logrec     : log record that written to local log file
+// @hostname   : log record originator
+// @networkname: network name
+// @recordId   : record id of @logrec
+// @sev        : severity of log record @logrec
+// @time       : Same timestamp as in the log record @logrec
+//<
+struct RecordData {
+  const char* name;
+  const char* logrec;
+  const char* hostname;
+  const char* networkname;
+  const char* appname;
+  bool isRtStream;
+  uint32_t recordId;
+  uint16_t sev;
+  timespec time;
+
+  RecordData() : name{nullptr}, logrec{nullptr}
+      , hostname{nullptr}
+      , networkname{nullptr}
+      , appname{nullptr}
+      , isRtStream{false}
+      , recordId{0}
+      , sev{0} {
+        time = base::ReadRealtimeClock();
+      }
+};
+
+//>
+// @WriteToDestination - Write @data To Destinations
+// @input:
+//   @data     : Carry necessary infos including log record.
+//   @destnames: Destination names wants to send log record to.
+// @return: true if successfully write to destinations. Oherwise, false.
+//
+// This function is blocking. The caller will be blocked until
+// the request is done.
+//<
+bool WriteToDestination(const RecordData& data,
+                        const VectorString& destnames);
+
+//>
+// @GetDestinationStatus - Get all destination status
+// @input : none
+// @output: vector of destination status with format
+//          {"name,status", "name1,status", etc.}
+//<
+VectorString GetDestinationStatus();
+
+//>
+// @DestinationHandler class: Dispatch msg to right destination
+// All msgs, if want to come to destination handlers,
+// must go though the DestinationHandler via
+// the interface DestinationHandler::DispachTo()
+//
+// Three kind of DestinationHandler messages supported.
+// 1) Msg contains destination configurations such as
+//    destination name, destination type and info (local socket path).
+//
+// 2) Msg contains log record which is going to write to destination.
+//    Beside log record, msg may contain other info such as
+//    the stream name(DN) it writting to, log record severity,
+//    log record originator, log record id, or timestamp.
+//
+// 3) Msg contains destinations that is going to be deleted.
+//    This message may have list of deleted destination names.
+//
+// The proper order of DestinationHandler messages should be:
+// (1) -> (2) -> (3) or (1) -> (3).
+//
+// So, if msg contains log record comes(2) to the DestinationHandler first
+// somehow (this probably due to an coding hole, that does not
+// have a good check before sending msg to Dispacher) before
+// the configurating destination msg(1), the DestinationHandler will drop
+// the message, and log info to syslog.
+// The same to the case if deleting destination msg(3) comes first
+// before configuring destination(1), that msg(3) will be drop
+// and log info to syslog.
+//
+// According to information in that messages, the DestinationHandler
+// will do its jobs: interpret the msg, take the right destination
+// and transfer that the msg to the destination type for futher processing.
+//<
+class DestinationHandler {
+ public:
+  enum { kMaxChar = 255 };
+  enum class DestType { kUnix = 0, kNilDest, kUnknown };
+  //>
+  // Handle message types
+  // kSendRecord: for writing log record to destination(s).
+  // kCfgDest   : for configuring destination(s).
+  // kDelDest   : for deleting destination(s).
+  // kNoDest    : for deleting all destination(s).
+  //<
+  enum class MsgType { kSendRecord = 0, kCfgDest, kDelDest, kNoDest };
+
+  //>
+  // RecordMsg hold necessary information to forming RFC5424 protocol.
+  // @severity  : log severity of sent log record.
+  // @record_id : record id attached to sent log record.
+  // @hash      : hash number generated from log stream name (DN)
+  // @time      : time attached to log record. Using realtime if no set.
+  // @origin    : node originator of the log record (host & network name).
+  // @log_record: the log record that writting to local file.
+  // @stream_dn : log stream name(DN).
+  //<
+  struct RecordInfo {
+    const char* stream_dn;
+    const char* msgid;
+    const char* origin;
+    const char* log_record;
+    const char* app_name;
+    uint16_t severity;
+    uint32_t record_id;
+    struct timespec time;
+
+    // Set default value
+    RecordInfo() : stream_dn{nullptr}
+        , msgid{nullptr}
+        , origin{nullptr}
+        , log_record{nullptr}
+        , app_name{nullptr}
+        , severity{0}
+        , record_id{0}  {
+          time = base::ReadRealtimeClock();
+    }
+  };
+
+  //>
+  // @RecordMsg - send record @rec to destination name @name
+  // @name: destination name
+  // @rec : record message
+  //>
+  struct RecordMsg {
+    char name[kMaxChar];
+    RecordInfo rec;
+    RecordMsg() : name{0}, rec{} {}
+  };
+
+  //>
+  // CfgDestMsg - holds information for one destination.
+  // @name : name of destination.
+  // @type : type of destination.
+  // @value: destination configuration
+  //<
+  struct CfgDestMsg {
+    char name[kMaxChar];
+    char type[kMaxChar];
+    char value[kMaxChar];
+    // Clear the values of all types
+    CfgDestMsg() : name{0}, type{0}, value{0} {}
+  };
+
+  //>
+  // DelDestMsg - hold information for one destintaion name
+  // @name: refer to the destination name that is going to delete.
+  //<
+  struct DelDestMsg {
+    char name[kMaxChar];
+    DelDestMsg() : name{0} {}
+  };
+
+  //>
+  // @HandleMsg - Dispatch message format
+  // @type: the value depends on @info
+  // 1) kSendRecord if @info is RecordMsg
+  // 2) kCfgDest if @info is CfgDestMsg
+  // 3) kDelDest if @info is DelDestMsg
+  // 4) kNoDest if not above
+  //<
+  struct HandleMsg {
+    MsgType type;
+    union {
+      RecordMsg rec;
+      CfgDestMsg cfg;
+      DelDestMsg del;
+    } info;
+  };
+
+  // Unique object instance of @DestinationHandler class
+  static DestinationHandler& Instance() { return me_; }
+  // Extract the stream name from stream DN
+  static std::string GenerateMsgId(const std::string&, bool);
+
+  // Do destination configuration basing on input @vdest and @type.
+  ErrCode ProcessCfgChange(const VectorString& vdest, ModifyType type);
+  // Do write log record @info to destinations @name
+  ErrCode ProcessWriteReq(const RecordInfo& info, const std::string& name);
+  // Get all destination status with format
+  // {"name,status", "name1,status1", etc.}
+  VectorString GetDestinationStatus();
+
+ private:
+  // typedef, aim for shorten declaration
+  using NameTypeMap = std::map<std::string, DestType>;
+  DestinationHandler() : nametype_map_{} {}
+  ~DestinationHandler() {}
+
+  // Dispatch the @HandleMsg msg to destination @name.
+  // Based on the destination @name, the method will find the right
+  // destination @type, then forward the message to for futher processing.
+  ErrCode DispatchTo(const HandleMsg& msg, const std::string& name);
+  // Pack @CfgDestMsg from the string @dest which have pure format
+  // "name;type;value".
+  void FormCfgDestMsg(const std::string& dest, CfgDestMsg* msg);
+  // Pack @DelDestMsg basing on pure destination format
+  // "name;type;value".
+  void FormDelDestMsg(const std::string& dest, DelDestMsg* msg);
+  // Form @CfgDestMsg and dispatch the msg.
+  ErrCode AddDestConfig(const VectorString& vdest);
+  // Form @DelDestMsg and dispatch the msg.
+  ErrCode DelDestConfig(const VectorString& vdest);
+  // Form @kNoDest dispatch msg
+  ErrCode DelAllCfgDest();
+  // Find name @name from @vec
+  bool VectorFind(const VectorString& vec, const std::string name);
+  // Return @DestType based on the destination string @type
+  DestType GetDestType(const std::string& type);
+  DestType ToDestType(const std::string& name);
+  // Update the @nametype_map_ whenever getting
+  // destination configuration change.
+  void UpdateDestTypeDb(const DestinationHandler::HandleMsg&);
+  // Update the `logRecordDestinationStatus`
+  void UpdateRtDestStatus();
+
+  // The map between destination @name and destination @type
+  NameTypeMap nametype_map_;
+  // Hold all existing destinations with pure format {"a;b;c", etc}
+  VectorString allcfgdests_map_;
+  static DestinationHandler me_;
+};
+
+#endif  // SRC_LOG_LOGD_LGS_DEST_H_
diff --git a/src/log/logd/lgs_evt.cc b/src/log/logd/lgs_evt.cc
--- a/src/log/logd/lgs_evt.cc
+++ b/src/log/logd/lgs_evt.cc
@@ -29,6 +29,7 @@
 #include "lgs_imm_gcfg.h"
 #include "base/osaf_extended_name.h"
 #include "lgs_clm.h"
+#include "lgs_dest.h"
 
 static uint32_t process_api_evt(lgsv_lgs_evt_t *evt);
 static uint32_t proc_lga_updn_mds_msg(lgsv_lgs_evt_t *evt);
@@ -1282,6 +1283,8 @@ static uint32_t proc_write_log_async_msg
   void *ckpt_ptr;
   uint32_t max_logrecsize = 0;
   char node_name[_POSIX_HOST_NAME_MAX];
+  RecordData data;
+  timespec time;
 
   memset(node_name, 0, _POSIX_HOST_NAME_MAX);
   strncpy(node_name, evt->node_name, _POSIX_HOST_NAME_MAX);
@@ -1341,6 +1344,35 @@ static uint32_t proc_write_log_async_msg
     goto done;
   }
 
+  //>
+  // Has successfully written log record to file.
+  // Now, send to destination if any destination name set.
+  //<
+
+  // Streaming not support on alarm/notif streams.
+  if ((stream->name == SA_LOG_STREAM_ALARM) ||
+      (stream->name == SA_LOG_STREAM_NOTIFICATION)) {
+    goto checkpoint;
+  }
+
+  // Packing Record data that carry necessary information
+  // to form RFC5424 syslog msg, then send to destination name(s).
+  data.name = stream->name.c_str();
+  data.logrec = logOutputString;
+  data.hostname = node_name;
+  data.networkname = lgs_get_networkname().c_str();
+  data.appname = osaf_extended_name_borrow(
+      param->logRecord->logHeader.genericHdr.logSvcUsrName);
+  data.isRtStream = stream->isRtStream;
+  data.recordId = stream->logRecordId;
+  data.sev = param->logRecord->logHeader.genericHdr.logSeverity;
+  time.tv_sec = (param->logRecord->logTimeStamp / (SaTimeT)SA_TIME_ONE_SECOND);
+  time.tv_nsec = (param->logRecord->logTimeStamp % 
(SaTimeT)SA_TIME_ONE_SECOND);
+  data.time = time;
+
+  WriteToDestination(data, stream->dest_names);
+
+checkpoint:
   /* TODO: send fail back if ack is wanted, Fix counter for application 
stream!! */
   if (cb->ha_state == SA_AMF_HA_ACTIVE) {
     if (lgs_is_peer_v2()) {
diff --git a/src/log/logd/lgs_imm.cc b/src/log/logd/lgs_imm.cc
--- a/src/log/logd/lgs_imm.cc
+++ b/src/log/logd/lgs_imm.cc
@@ -45,12 +45,14 @@
 #include "log/logd/lgs_file.h"
 #include "log/logd/lgs_recov.h"
 #include "log/logd/lgs_config.h"
+#include "log/logd/lgs_dest.h"
 #include "base/saf_error.h"
 
 #include "lgs_mbcsv_v1.h"
 #include "lgs_mbcsv_v2.h"
 #include "lgs_mbcsv_v3.h"
 #include "lgs_mbcsv_v5.h"
+#include "lgs_mbcsv_v6.h"
 
 /* TYPE DEFINITIONS
  * ----------------
@@ -234,11 +236,33 @@ static uint32_t ckpt_stream_config(log_s
   uint32_t rc;
   lgsv_ckpt_msg_v1_t ckpt_v1;
   lgsv_ckpt_msg_v2_t ckpt_v2;
+  lgsv_ckpt_msg_v6_t ckpt_v3;
+
   void *ckpt_ptr;
 
   TRACE_ENTER();
 
-  if (lgs_is_peer_v2()) {
+  if (lgs_is_peer_v6()) {
+    memset(&ckpt_v3, 0, sizeof(ckpt_v3));
+    ckpt_v3.header.ckpt_rec_type = LGS_CKPT_CFG_STREAM;
+    ckpt_v3.header.num_ckpt_records = 1;
+    ckpt_v3.header.data_len = 1;
+
+    ckpt_v3.ckpt_rec.stream_cfg.name = const_cast<char 
*>(stream->name.c_str());
+    ckpt_v3.ckpt_rec.stream_cfg.fileName = const_cast<char 
*>(stream->fileName.c_str());
+    ckpt_v3.ckpt_rec.stream_cfg.pathName = const_cast<char 
*>(stream->pathName.c_str());
+    ckpt_v3.ckpt_rec.stream_cfg.maxLogFileSize = stream->maxLogFileSize;
+    ckpt_v3.ckpt_rec.stream_cfg.fixedLogRecordSize = 
stream->fixedLogRecordSize;
+    ckpt_v3.ckpt_rec.stream_cfg.logFullAction = stream->logFullAction;
+    ckpt_v3.ckpt_rec.stream_cfg.logFullHaltThreshold = 
stream->logFullHaltThreshold;
+    ckpt_v3.ckpt_rec.stream_cfg.maxFilesRotated = stream->maxFilesRotated;
+    ckpt_v3.ckpt_rec.stream_cfg.logFileFormat = stream->logFileFormat;
+    ckpt_v3.ckpt_rec.stream_cfg.severityFilter = stream->severityFilter;
+    ckpt_v3.ckpt_rec.stream_cfg.logFileCurrent = const_cast<char 
*>(stream->logFileCurrent.c_str());
+    ckpt_v3.ckpt_rec.stream_cfg.dest_names = const_cast<char 
*>(stream->stb_dest_names.c_str());
+    ckpt_v3.ckpt_rec.stream_cfg.c_file_close_time_stamp = 
stream->act_last_close_timestamp;
+    ckpt_ptr = &ckpt_v3;
+  } else if (lgs_is_peer_v2()) {
     memset(&ckpt_v2, 0, sizeof(ckpt_v2));
     ckpt_v2.header.ckpt_rec_type = LGS_CKPT_CFG_STREAM;
     ckpt_v2.header.num_ckpt_records = 1;
@@ -856,13 +880,14 @@ static SaAisErrorT config_ccb_completed_
       // Note: Multi value attribute
       TRACE("logRecordDestinationConfiguration. Values number = %d",
             attribute->attrValuesNumber);
-      std::vector<std::string> values_vector;
+      std::vector<std::string> values_vector{};
       for (uint32_t i=0; i < attribute->attrValuesNumber; i++) {
         value = attribute->attrValues[i];
         char *value_str = *(reinterpret_cast<char **>(value));
         values_vector.push_back(value_str);
       }
-      rc = lgs_cfg_verify_log_record_destination_configuration(values_vector);
+      rc = lgs_cfg_verify_log_record_destination_configuration(
+          values_vector, attrMod->modType);
       if (rc == -1) {
         report_oi_error(immOiHandle, opdata->ccbId,
                         "%s value is NOT accepted", attribute->attrName);
@@ -1270,6 +1295,29 @@ done:
   return rc;
 }
 
+static bool is_valid_dest_names(const std::vector<std::string>& names) {
+  // Stream has no destination name
+  if (names.size() == 0) return true;
+  for (const auto& name : names) {
+    // Empty destination name is invalid
+    if (name.empty() == true || name.size() == 0) {
+      TRACE("%s name is empty", __func__);
+      return false;
+    }
+    // Contain special characters is not allowed
+    if (logutil::isValidName(name) == false) {
+      TRACE("%s name has special chars in", __func__);
+      return false;
+    }
+    // Name is too long
+    if (name.length() > 255) {
+      TRACE("%s too long name", __func__);
+      return false;
+    }
+  }
+  return true;
+}
+
 /**
  * Validate input parameters creation and modify of a persistent stream
  * i.e. a stream that has a configuration object.
@@ -1368,12 +1416,16 @@ static SaAisErrorT check_attr_validity(S
     if (attribute->attrValuesNumber > 0) {
       value = attribute->attrValues[0];
     } else if (opdata->operationType == CCBUTIL_MODIFY) {
-      /* An attribute without a value is never valid if modify */
-      report_oi_error(immOiHandle, opdata->ccbId,
-                      "Attribute %s has no value",attribute->attrName);
-      TRACE("Modify: Attribute %s has no value",attribute->attrName);
-      rc = SA_AIS_ERR_BAD_OPERATION;
-      goto done;
+      if (!strcmp(attribute->attrName, "saLogRecordDestination")) {
+        // do nothing
+      } else {
+        /* An attribute without a value is never valid if modify */
+        report_oi_error(immOiHandle, opdata->ccbId,
+                        "Attribute %s has no value",attribute->attrName);
+        TRACE("Modify: Attribute %s has no value",attribute->attrName);
+        rc = SA_AIS_ERR_BAD_OPERATION;
+        goto done;
+      }
     } else {
       /* If create all attributes will be present also the ones without
        * any value.
@@ -1428,8 +1480,24 @@ static SaAisErrorT check_attr_validity(S
       i_severityFilter_mod = true;
       TRACE("Saved attribute \"%s\" = %d", attribute->attrName,
             i_severityFilter);
+    } else if (!strcmp(attribute->attrName,"saLogRecordDestination")) {
+      std::vector<std::string> vstring{};
+      for (unsigned i = 0; i < attribute->attrValuesNumber; i++) {
+        value = attribute->attrValues[i];
+        char *value_str = *(reinterpret_cast<char **>(value));
+        vstring.push_back(value_str);
+      }
+      if (is_valid_dest_names(vstring) == false) {
+        /* Report failed if has special character in file name */
+        rc = SA_AIS_ERR_BAD_OPERATION;
+        report_oi_error(immOiHandle, opdata->ccbId,
+                        "Invalid saLogRecordDestination value");
+        TRACE("Invalid saLogRecordDestination value");
+        goto done;
+      }
     }
 
+
     /* Get next attribute or detect no more attributes */
  next:
     if (opdata->operationType == CCBUTIL_CREATE) {
@@ -1976,6 +2044,41 @@ static void apply_conf_logDataGroupname(
   logDataGroupname_fileown(value_ptr);
 }
 
+static void apply_config_destinations_change(
+    const std::vector<std::string>& vdestcfg,
+    SaImmAttrModificationTypeT type,
+    lgs_config_chg_t* config_data) {
+  switch (type) {
+    case SA_IMM_ATTR_VALUES_ADD:
+      // Configure destinations
+      CfgDestination(vdestcfg, ModifyType::kAdd);
+      lgs_cfgupd_multival_add(LOG_RECORD_DESTINATION_CONFIGURATION,
+                              vdestcfg,
+                              config_data);
+      break;
+
+    case SA_IMM_ATTR_VALUES_DELETE:
+      // Configure destinations
+      CfgDestination(vdestcfg, ModifyType::kDelete);
+      lgs_cfgupd_multival_delete(LOG_RECORD_DESTINATION_CONFIGURATION,
+                                 vdestcfg,
+                                 config_data);
+      break;
+
+    case SA_IMM_ATTR_VALUES_REPLACE:
+      CfgDestination(vdestcfg, ModifyType::kReplace);
+      lgs_cfgupd_mutival_replace(LOG_RECORD_DESTINATION_CONFIGURATION,
+                                 vdestcfg,
+                                 config_data);
+      break;
+
+    default:
+      // Shall never happen
+      LOG_ER("%s: Unknown modType %d", __FUNCTION__, type);
+      osafassert(0);
+  };
+}
+
 /**
  * Apply changes. Validation is not needed here since all validation is done in
  * the complete callback
@@ -2090,28 +2193,9 @@ static void config_ccb_apply_modify(cons
         char *value_str = *(reinterpret_cast<char **>(value));
         values_vector.push_back(value_str);
       }
-
-      switch (attrMod->modType) {
-        case SA_IMM_ATTR_VALUES_ADD:
-          lgs_cfgupd_multival_add(LOG_RECORD_DESTINATION_CONFIGURATION,
-                                  values_vector,
-                                  &config_data);
-          break;
-        case SA_IMM_ATTR_VALUES_DELETE:
-          lgs_cfgupd_multival_delete(LOG_RECORD_DESTINATION_CONFIGURATION,
-                                  values_vector,
-                                  &config_data);
-          break;
-        case SA_IMM_ATTR_VALUES_REPLACE:
-          lgs_cfgupd_mutival_replace(LOG_RECORD_DESTINATION_CONFIGURATION,
-                                  values_vector,
-                                  &config_data);
-          break;
-        default:
-          // Shall never happen
-          LOG_ER("%s: Unknown modType %d", __FUNCTION__, attrMod->modType);
-          osafassert(0);
-      };
+      apply_config_destinations_change(values_vector,
+                                       attrMod->modType,
+                                       &config_data);
     }
 
     attrMod = opdata->param.modify.attrMods[i++];
@@ -2252,6 +2336,15 @@ static SaAisErrorT stream_create_and_con
       } else if (!strcmp(ccb->param.create.attrValues[i]->attrName, 
"saLogStreamSeverityFilter")) {
         (*stream)->severityFilter = *((SaUint32T *) value);
         TRACE("severityFilter: %u", (*stream)->severityFilter);
+      } else if (!strcmp(ccb->param.create.attrValues[i]->attrName,
+                          "saLogRecordDestination")) {
+        std::vector<std::string> vstring{};
+        for (unsigned ii = 0; ii < 
ccb->param.create.attrValues[i]->attrValuesNumber; ii++) {
+          value = ccb->param.create.attrValues[i]->attrValues[ii];
+          char *value_str = *(reinterpret_cast<char **>(value));
+          vstring.push_back(value_str);
+        }
+        log_stream_add_dest_name(*stream, vstring);
       }
     }
     i++;
@@ -2306,6 +2399,30 @@ static void stream_ccb_apply_create(cons
   TRACE_LEAVE();
 }
 
+static void apply_destination_names_change(
+    log_stream_t* stream,
+    const std::vector<std::string>& destname,
+    SaImmAttrModificationTypeT type) {
+  switch (type) {
+    case SA_IMM_ATTR_VALUES_ADD:
+      log_stream_add_dest_name(stream, destname);
+      break;
+
+    case SA_IMM_ATTR_VALUES_DELETE:
+      log_stream_delete_dest_name(stream, destname);
+      break;
+
+    case SA_IMM_ATTR_VALUES_REPLACE:
+      log_stream_replace_dest_name(stream, destname);
+      break;
+
+    default:
+      // Shall never happen
+      LOG_ER("%s: Unknown modType %d", __FUNCTION__, type);
+      osafassert(0);
+  }
+}
+
 static void stream_ccb_apply_modify(const CcbUtilOperationData_t *opdata) {
   const SaImmAttrModificationT_2 *attrMod;
   int i = 0;
@@ -2325,12 +2442,21 @@ static void stream_ccb_apply_modify(cons
 
   attrMod = opdata->param.modify.attrMods[i++];
   while (attrMod != NULL) {
-    void *value;
+    void *value = nullptr;
     const SaImmAttrValuesT_2 *attribute = &attrMod->modAttr;
 
     TRACE("attribute %s", attribute->attrName);
-
-    value = attribute->attrValues[0];
+    if (attribute->attrValuesNumber != 0) {
+      value = attribute->attrValues[0];
+    } else {
+      if (!strcmp(attribute->attrName, "saLogRecordDestination")) {
+        const std::vector<std::string> dname{};
+        LOG_NO("%s deleted", __func__);
+        log_stream_delete_dest_name(stream, dname);
+        attrMod = opdata->param.modify.attrMods[i++];
+        continue;
+      }
+    }
 
     if (!strcmp(attribute->attrName, "saLogStreamFileName")) {
       fileName = *((char **)value);
@@ -2369,6 +2495,14 @@ static void stream_ccb_apply_modify(cons
       if (stream->streamType != STREAM_TYPE_ALARM &&
                 stream->streamType != STREAM_TYPE_NOTIFICATION)
         lgs_send_severity_filter_to_clients(stream->streamId, severityFilter);
+    } else if (!strcmp(attribute->attrName, "saLogRecordDestination")) {
+      std::vector<std::string> vstring{};
+      for (unsigned i = 0; i < attribute->attrValuesNumber; i++) {
+        value = attribute->attrValues[i];
+        char *value_str = *(reinterpret_cast<char **>(value));
+        vstring.push_back(value_str);
+      }
+      apply_destination_names_change(stream, vstring, attrMod->modType);
     } else {
       LOG_ER("Error: Unknown attribute name");
       osafassert(0);
@@ -2620,6 +2754,7 @@ static SaAisErrorT stream_create_and_con
     const_cast<char *>("saLogStreamLogFullHaltThreshold"),
     const_cast<char *>("saLogStreamMaxFilesRotated"),
     const_cast<char *>("saLogStreamLogFileFormat"),
+    const_cast<char *>("saLogRecordDestination"),
     const_cast<char *>("saLogStreamSeverityFilter"),
     const_cast<char *>("saLogStreamCreationTimestamp"),
     NULL
@@ -2703,6 +2838,14 @@ static SaAisErrorT stream_create_and_con
     } else if (!strcmp(attribute->attrName, "saLogStreamSeverityFilter")) {
       stream->severityFilter = *((SaUint32T *)value);
       TRACE("severityFilter: %u", stream->severityFilter);
+    } else if (!strcmp(attribute->attrName, "saLogRecordDestination")) {
+      std::vector<std::string> vstring{};
+      for (unsigned i = 0; i < attribute->attrValuesNumber; i++) {
+        value = attribute->attrValues[i];
+        char *value_str = *(reinterpret_cast<char **>(value));
+        vstring.push_back(value_str);
+      }
+      log_stream_add_dest_name(stream, vstring);
     } else if (!strcmp(attribute->attrName, "saLogStreamCreationTimestamp")) {
       if (attribute->attrValuesNumber != 0) {
         /* Restore creation timestamp if exist
diff --git a/src/log/logd/lgs_main.cc b/src/log/logd/lgs_main.cc
--- a/src/log/logd/lgs_main.cc
+++ b/src/log/logd/lgs_main.cc
@@ -1,6 +1,7 @@
-/*      -*- OpenSAF  -*-
+ /*      -*- OpenSAF  -*-
  *
  * (C) Copyright 2008-2010 The OpenSAF Foundation
+ * Copyright Ericsson AB 2018, 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
@@ -39,6 +40,7 @@
 #include "lgs_recov.h"
 #include "osaf/immutil/immutil.h"
 #include "lgs_clm.h"
+#include "log/logd/lgs_dest.h"
 
 /* ========================================================================
  *   DEFINITIONS
@@ -371,6 +373,7 @@ done:
 uint32_t initialize_for_assignment(lgs_cb_t *cb, SaAmfHAStateT ha_state) {
   const char *logsv_root_dir = NULL;
   const char *logsv_data_groupname = NULL;
+  const std::vector<std::string> *vdest = nullptr;
   TRACE_ENTER2("ha_state = %d", (int) ha_state);
   uint32_t rc = NCSCC_RC_SUCCESS;
 
@@ -393,6 +396,12 @@ uint32_t initialize_for_assignment(lgs_c
   logsv_data_groupname = static_cast<const char 
*>(lgs_cfg_get(LGS_IMM_DATA_GROUPNAME));
   LOG_NO("LOG root directory is: \"%s\"", logsv_root_dir);
   LOG_NO("LOG data group is: \"%s\"", logsv_data_groupname);
+  vdest = reinterpret_cast<const std::vector<std::string>*>(
+      lgs_cfg_get(LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION));
+  osafassert(vdest != nullptr);
+  if (vdest->size() > 0) {
+    CfgDestination(*vdest, ModifyType::kAdd);
+  }
 
   /* Initialize file handling thread
    * Configuration must have been initialized
diff --git a/src/log/logd/lgs_mbcsv.cc b/src/log/logd/lgs_mbcsv.cc
--- a/src/log/logd/lgs_mbcsv.cc
+++ b/src/log/logd/lgs_mbcsv.cc
@@ -21,12 +21,14 @@
 #include "base/ncssysf_mem.h"
 #include "base/osaf_time.h"
 
-#include "lgs_mbcsv_v6.h"
-#include "lgs_mbcsv_v5.h"
-#include "lgs_mbcsv_v3.h"
-#include "lgs_mbcsv_v2.h"
-#include "lgs_mbcsv_v1.h"
-#include "lgs_recov.h"
+#include "osaf/immutil/immutil.h"
+#include "log/logd/lgs_dest.h"
+#include "log/logd/lgs_mbcsv_v6.h"
+#include "log/logd/lgs_mbcsv_v5.h"
+#include "log/logd/lgs_mbcsv_v3.h"
+#include "log/logd/lgs_mbcsv_v2.h"
+#include "log/logd/lgs_mbcsv_v1.h"
+#include "log/logd/lgs_recov.h"
 
 /*
   LGS_CKPT_DATA_HEADER
@@ -125,43 +127,83 @@ uint32_t edp_ed_open_stream_rec(EDU_HDL 
                                 EDU_BUF_ENV *buf_env, EDP_OP_TYPE op, EDU_ERR 
*o_err) {
   uint32_t rc = NCSCC_RC_SUCCESS;
   lgs_ckpt_stream_open_t *ckpt_open_stream_msg_ptr = NULL, 
**ckpt_open_stream_msg_dec_ptr;
+  if (lgs_is_peer_v6()) {
+    EDU_INST_SET ckpt_open_stream_rec_ed_rules[] = {
+      {EDU_START, edp_ed_open_stream_rec, 0, 0, 0, 
sizeof(lgs_ckpt_stream_open_t), 0, NULL},
+      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->streamId, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->clientId, 0, NULL},
+      {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logFile, 0, NULL},
+      {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logPath, 0, NULL},
+      {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logFileCurrent, 0, NULL},
+      {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->dest_names, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->maxFileSize, 0, NULL},
+      {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->maxLogRecordSize, 0, NULL},
+      {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logFileFullAction, 0, NULL},
+      {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->maxFilesRotated, 0, NULL},
+      {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->fileFmt, 0, NULL},
+      {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logStreamName, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->creationTimeStamp, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->numOpeners, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->streamType, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logRecordId, 0, NULL},
+      {EDU_END, 0, 0, 0, 0, 0, 0, NULL},
+    };
 
-  EDU_INST_SET ckpt_open_stream_rec_ed_rules[] = {
-    {EDU_START, edp_ed_open_stream_rec, 0, 0, 0, 
sizeof(lgs_ckpt_stream_open_t), 0, NULL},
-    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->streamId, 0, NULL},
-    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->clientId, 0, NULL},
-    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logFile, 0, NULL},
-    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logPath, 0, NULL},
-    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logFileCurrent, 0, NULL},
-    {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->maxFileSize, 0, NULL},
-    {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->maxLogRecordSize, 0, NULL},
-    {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logFileFullAction, 0, NULL},
-    {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->maxFilesRotated, 0, NULL},
-    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->fileFmt, 0, NULL},
-    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logStreamName, 0, NULL},
-    {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->creationTimeStamp, 0, NULL},
-    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->numOpeners, 0, NULL},
-    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->streamType, 0, NULL},
-    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logRecordId, 0, NULL},
-    {EDU_END, 0, 0, 0, 0, 0, 0, NULL},
-  };
+    if (op == EDP_OP_TYPE_ENC) {
+      ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t *>(ptr);
+    } else if (op == EDP_OP_TYPE_DEC) {
+      ckpt_open_stream_msg_dec_ptr = static_cast<lgs_ckpt_stream_open_t 
**>(ptr);
+      if (*ckpt_open_stream_msg_dec_ptr == NULL) {
+        *o_err = EDU_ERR_MEM_FAIL;
+        return NCSCC_RC_FAILURE;
+      }
+      memset(*ckpt_open_stream_msg_dec_ptr, '\0', 
sizeof(lgs_ckpt_stream_open_t));
+      ckpt_open_stream_msg_ptr = *ckpt_open_stream_msg_dec_ptr;
+    } else {
+      ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t *>(ptr);
+    }
 
-  if (op == EDP_OP_TYPE_ENC) {
-    ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t *>(ptr);
-  } else if (op == EDP_OP_TYPE_DEC) {
-    ckpt_open_stream_msg_dec_ptr = static_cast<lgs_ckpt_stream_open_t **>(ptr);
-    if (*ckpt_open_stream_msg_dec_ptr == NULL) {
-      *o_err = EDU_ERR_MEM_FAIL;
-      return NCSCC_RC_FAILURE;
+    rc = m_NCS_EDU_RUN_RULES(edu_hdl, edu_tkn, ckpt_open_stream_rec_ed_rules, 
ckpt_open_stream_msg_ptr,
+                             ptr_data_len, buf_env, op, o_err);
+  } else {
+    EDU_INST_SET ckpt_open_stream_rec_ed_rules[] = {
+      {EDU_START, edp_ed_open_stream_rec, 0, 0, 0, 
sizeof(lgs_ckpt_stream_open_t), 0, NULL},
+      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->streamId, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->clientId, 0, NULL},
+      {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logFile, 0, NULL},
+      {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logPath, 0, NULL},
+      {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logFileCurrent, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->maxFileSize, 0, NULL},
+      {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->maxLogRecordSize, 0, NULL},
+      {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logFileFullAction, 0, NULL},
+      {EDU_EXEC, ncs_edp_int32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->maxFilesRotated, 0, NULL},
+      {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->fileFmt, 0, NULL},
+      {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logStreamName, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->creationTimeStamp, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->numOpeners, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->streamType, 0, NULL},
+      {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_open_t 
*)0)->logRecordId, 0, NULL},
+      {EDU_END, 0, 0, 0, 0, 0, 0, NULL},
+    };
+
+    if (op == EDP_OP_TYPE_ENC) {
+      ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t *>(ptr);
+    } else if (op == EDP_OP_TYPE_DEC) {
+      ckpt_open_stream_msg_dec_ptr = static_cast<lgs_ckpt_stream_open_t 
**>(ptr);
+      if (*ckpt_open_stream_msg_dec_ptr == NULL) {
+        *o_err = EDU_ERR_MEM_FAIL;
+        return NCSCC_RC_FAILURE;
+      }
+      memset(*ckpt_open_stream_msg_dec_ptr, '\0', 
sizeof(lgs_ckpt_stream_open_t));
+      ckpt_open_stream_msg_ptr = *ckpt_open_stream_msg_dec_ptr;
+    } else {
+      ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t *>(ptr);
     }
-    memset(*ckpt_open_stream_msg_dec_ptr, '\0', 
sizeof(lgs_ckpt_stream_open_t));
-    ckpt_open_stream_msg_ptr = *ckpt_open_stream_msg_dec_ptr;
-  } else {
-    ckpt_open_stream_msg_ptr = static_cast<lgs_ckpt_stream_open_t *>(ptr);
+
+    rc = m_NCS_EDU_RUN_RULES(edu_hdl, edu_tkn, ckpt_open_stream_rec_ed_rules, 
ckpt_open_stream_msg_ptr,
+                             ptr_data_len, buf_env, op, o_err);
   }
 
-  rc = m_NCS_EDU_RUN_RULES(edu_hdl, edu_tkn, ckpt_open_stream_rec_ed_rules, 
ckpt_open_stream_msg_ptr,
-                           ptr_data_len, buf_env, op, o_err);
   return rc;
 }       /* End edp_ed_open_stream_rec */
 /* End of EDU encode/decode functions */
@@ -598,6 +640,7 @@ uint32_t lgs_ckpt_stream_open_set(log_st
   stream_open->logFileCurrent = const_cast<char 
*>(logStream->logFileCurrent.c_str());
   stream_open->fileFmt = logStream->logFileFormat;
   stream_open->logStreamName = const_cast<char *>(logStream->name.c_str());
+  stream_open->dest_names = const_cast<char 
*>(logStream->stb_dest_names.c_str());
   stream_open->maxFileSize = logStream->maxLogFileSize;
   stream_open->maxLogRecordSize = logStream->fixedLogRecordSize;
   stream_open->logFileFullAction = logStream->logFullAction;
@@ -1070,7 +1113,11 @@ static uint32_t ckpt_decode_log_cfg_stre
   void *stream_cfg;
   EDU_PROG_HANDLER edp_function;
 
-  if (lgs_is_peer_v2()) {
+  if (lgs_is_peer_v6()) {
+    lgsv_ckpt_msg_v6_t *ckpt_msg_v6 = static_cast<lgsv_ckpt_msg_v6_t 
*>(ckpt_msg);
+    stream_cfg = &ckpt_msg_v6->ckpt_rec.stream_cfg;
+    edp_function = edp_ed_cfg_stream_rec_v6;
+  } else if (lgs_is_peer_v2()) {
     lgsv_ckpt_msg_v2_t *ckpt_msg_v2 = static_cast<lgsv_ckpt_msg_v2_t 
*>(ckpt_msg);
     stream_cfg = &ckpt_msg_v2->ckpt_rec.stream_cfg;
     edp_function = edp_ed_cfg_stream_rec_v2;
@@ -1092,11 +1139,16 @@ static uint32_t ckpt_decode_log_cfg(lgs_
   uint32_t rc = NCSCC_RC_SUCCESS;
   void *lgs_cfg = NULL;
   EDU_PROG_HANDLER edp_function = NULL;
+  lgsv_ckpt_msg_v6_t *ckpt_msg_v6;
   lgsv_ckpt_msg_v5_t *ckpt_msg_v5;
   lgsv_ckpt_msg_v3_t *ckpt_msg_v3;
   lgsv_ckpt_msg_v2_t *ckpt_msg_v2;
 
-  if (lgs_is_peer_v5()) {
+  if (lgs_is_peer_v6()) {
+    ckpt_msg_v6 = static_cast<lgsv_ckpt_msg_v6_t *>(ckpt_msg);
+    lgs_cfg = &ckpt_msg_v6->ckpt_rec.lgs_cfg;
+    edp_function = edp_ed_lgs_cfg_rec_v5;
+  } else if (lgs_is_peer_v5()) {
     ckpt_msg_v5 = static_cast<lgsv_ckpt_msg_v5_t *>(ckpt_msg);
     lgs_cfg = &ckpt_msg_v5->ckpt_rec.lgs_cfg;
     edp_function = edp_ed_lgs_cfg_rec_v5;
@@ -1159,7 +1211,7 @@ static uint32_t ckpt_decode_async_update
   if (lgs_is_peer_v6()) {
     ckpt_msg_v6->header = hdr;
     ckpt_msg = ckpt_msg_v6;
-  } else   if (lgs_is_peer_v5()) {
+  } else if (lgs_is_peer_v5()) {
     ckpt_msg_v5->header = hdr;
     ckpt_msg = ckpt_msg_v5;
   } else if (lgs_is_peer_v4()) {
@@ -1180,7 +1232,7 @@ static uint32_t ckpt_decode_async_update
       if (lgs_is_peer_v6()) {
         reg_rec = &ckpt_msg_v6->ckpt_rec.initialize_client;
         edp_function_reg = edp_ed_reg_rec_v6;
-      }  else if (lgs_is_peer_v5()) {
+      } else if (lgs_is_peer_v5()) {
         reg_rec = &ckpt_msg_v5->ckpt_rec.initialize_client;
         edp_function_reg = edp_ed_reg_rec;
       } else if (lgs_is_peer_v4()) {
@@ -1906,10 +1958,14 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t 
   lgs_ckpt_stream_open_t *param;
   log_stream_t *stream;
   int pos = 0, err = 0;
+  SaNameT objectName;
 
   TRACE_ENTER();
 
-  if (lgs_is_peer_v2()) {
+  if (lgs_is_peer_v6()) {
+    lgsv_ckpt_msg_v6_t *data_v6 = static_cast<lgsv_ckpt_msg_v6_t *>(data);
+    param = &data_v6->ckpt_rec.stream_open;
+  } else if (lgs_is_peer_v2()) {
     lgsv_ckpt_msg_v2_t *data_v2 = static_cast<lgsv_ckpt_msg_v2_t *>(data);
     param = &data_v2->ckpt_rec.stream_open;
   } else {
@@ -1974,7 +2030,27 @@ uint32_t ckpt_proc_open_stream(lgs_cb_t 
     stream->logFileCurrent = param->logFileCurrent;
     stream->stb_prev_actlogFileCurrent = param->logFileCurrent;
     stream->stb_logFileCurrent = param->logFileCurrent;
-    stream->isRtStream = SA_TRUE;
+
+    osaf_extended_name_lend(param->logStreamName, &objectName);
+    SaImmClassNameT className = immutil_get_className(&objectName);
+    if (className != nullptr && strcmp(className, "SaLogStreamConfig") == 0) {
+      stream->isRtStream = SA_FALSE;
+    } else {
+      stream->isRtStream = SA_TRUE;
+    }
+    if (className != nullptr) free(className);
+
+    // Only update destination names if peer is v6 or upper.
+    if (lgs_is_peer_v6()) {
+      if (param->dest_names != nullptr && strlen(param->dest_names) != 0) {
+        TRACE("Dest_name %s", param->dest_names);
+        stream->stb_dest_names = std::string{param->dest_names};
+        stream->dest_names = logutil::Parser(stream->stb_dest_names, ";");
+      } else {
+        stream->stb_dest_names = "";
+        stream->dest_names.clear();
+      }
+    }
   }
 
   /* If configured for split file system files shall be opened on stand by
@@ -2133,12 +2209,28 @@ static uint32_t ckpt_proc_cfg_stream(lgs
   SaUint32T logFullHaltThreshold; /* !app log stream */
   SaUint32T maxFilesRotated;
   char *logFileFormat;
+  char *dest_names = nullptr;
   SaUint32T severityFilter;
   char *logFileCurrent;
 
   TRACE_ENTER();
 
-  if (lgs_is_peer_v2()) {
+  if (lgs_is_peer_v6()) {
+    lgsv_ckpt_msg_v6_t *data_v6 = static_cast<lgsv_ckpt_msg_v6_t *>(data);
+    name = data_v6->ckpt_rec.stream_cfg.name;
+    fileName = data_v6->ckpt_rec.stream_cfg.fileName;
+    pathName = data_v6->ckpt_rec.stream_cfg.pathName;
+    maxLogFileSize = data_v6->ckpt_rec.stream_cfg.maxLogFileSize;
+    fixedLogRecordSize = data_v6->ckpt_rec.stream_cfg.fixedLogRecordSize;
+    logFullAction = data_v6->ckpt_rec.stream_cfg.logFullAction;
+    logFullHaltThreshold = data_v6->ckpt_rec.stream_cfg.logFullHaltThreshold;
+    maxFilesRotated = data_v6->ckpt_rec.stream_cfg.maxFilesRotated;
+    logFileFormat = data_v6->ckpt_rec.stream_cfg.logFileFormat;
+    severityFilter = data_v6->ckpt_rec.stream_cfg.severityFilter;
+    logFileCurrent = data_v6->ckpt_rec.stream_cfg.logFileCurrent;
+    dest_names = data_v6->ckpt_rec.stream_cfg.dest_names;
+    closetime = data_v6->ckpt_rec.stream_cfg.c_file_close_time_stamp;
+  } else if (lgs_is_peer_v2()) {
     lgsv_ckpt_msg_v2_t *data_v2 = static_cast<lgsv_ckpt_msg_v2_t *>(data);
     name = data_v2->ckpt_rec.stream_cfg.name;
     fileName = data_v2->ckpt_rec.stream_cfg.fileName;
@@ -2191,6 +2283,17 @@ static uint32_t ckpt_proc_cfg_stream(lgs
   strcpy(stream->logFileFormat, logFileFormat);
   stream->severityFilter = severityFilter;
   stream->logFileCurrent = logFileCurrent;
+  // Only update destination name if peer is v6 or upper.
+  if (lgs_is_peer_v6()) {
+    if (dest_names != nullptr && strlen(dest_names) != 0) {
+      TRACE("dest_names: %s", dest_names);
+      stream->stb_dest_names = std::string{dest_names};
+      stream->dest_names = logutil::Parser(stream->stb_dest_names, ";");
+    } else {
+      stream->stb_dest_names = "";
+      stream->dest_names.clear();
+    }
+  }
 
   /* If split file mode, update standby files */
   if (lgs_is_split_file_system()) {
diff --git a/src/log/logd/lgs_mbcsv.h b/src/log/logd/lgs_mbcsv.h
--- a/src/log/logd/lgs_mbcsv.h
+++ b/src/log/logd/lgs_mbcsv.h
@@ -98,6 +98,7 @@ typedef struct {
   uint64_t creationTimeStamp;
   uint32_t numOpeners;
   char *logFileCurrent;
+  char *dest_names;
   logStreamTypeT streamType;
   uint32_t logRecordId;   /* log record identifier increased for each record */
 } lgs_ckpt_stream_open_t;
diff --git a/src/log/logd/lgs_mbcsv_v5.cc b/src/log/logd/lgs_mbcsv_v5.cc
--- a/src/log/logd/lgs_mbcsv_v5.cc
+++ b/src/log/logd/lgs_mbcsv_v5.cc
@@ -1,6 +1,7 @@
 /*      -*- OpenSAF  -*-
  *
  * (C) Copyright 2015 The OpenSAF Foundation
+ * Copyright Ericsson AB 2015, 2017 - All Rights Reserved.
  * File:   lgs_mbcsv_v5.c
  *
  * This program is distributed in the hope that it will be useful, but
@@ -22,6 +23,7 @@
  */
 
 #include "lgs_mbcsv_v5.h"
+#include "log/logd/lgs_dest.h"
 
 /****************************************************************************
  * Name          : ckpt_proc_lgs_cfg
@@ -133,6 +135,15 @@ uint32_t ckpt_proc_lgs_cfg_v5(lgs_cb_t *
     (void) lgs_configure_mailbox();
   }
 
+  // Only support destination configuration since V6
+  if (lgs_is_peer_v6()) {
+    const VectorString *vdest = nullptr;
+    vdest = reinterpret_cast<const VectorString*>(
+        lgs_cfg_get(LGS_IMM_LOG_RECORD_DESTINATION_CONFIGURATION));
+    osafassert(vdest != nullptr);
+    CfgDestination(*vdest, ModifyType::kReplace);
+  }
+
   /* Free buffer allocated by the EDU encoder */
   if (param->buffer == NULL) {
     LOG_ER("%s chkpt_buffer_ptr is NULL!", __FUNCTION__);
diff --git a/src/log/logd/lgs_mbcsv_v6.cc b/src/log/logd/lgs_mbcsv_v6.cc
--- a/src/log/logd/lgs_mbcsv_v6.cc
+++ b/src/log/logd/lgs_mbcsv_v6.cc
@@ -77,6 +77,72 @@ uint32_t edp_ed_reg_rec_v6(EDU_HDL *edu_
 }       /* End edp_ed_reg_rec */
 
 /****************************************************************************
+ * Name          : edp_ed_cfg_stream_rec
+ *
+ * Description   : This function is an EDU program for encoding/decoding
+ *                 lgsv checkpoint cfg_update_stream log rec.
+ *
+ * Arguments     : EDU_HDL - pointer to edu handle,
+ *                 EDU_TKN - internal edu token to help encode/decode,
+ *                 POINTER to the structure to encode/decode from/to,
+ *                 data length specifying number of structures,
+ *                 EDU_BUF_ENV - pointer to buffer for encoding/decoding.
+ *                 op - operation type being encode/decode.
+ *                 EDU_ERR - out param to indicate errors in processing.
+ *
+ * Return Values : NCSCC_RC_SUCCESS/NCSCC_RC_FAILURE
+ *
+ * Notes         : None.
+ *****************************************************************************/
+
+uint32_t edp_ed_cfg_stream_rec_v6(EDU_HDL *edu_hdl, EDU_TKN *edu_tkn,
+                                  NCSCONTEXT ptr, uint32_t *ptr_data_len,
+                                  EDU_BUF_ENV *buf_env, EDP_OP_TYPE op, 
EDU_ERR *o_err) {
+  uint32_t rc = NCSCC_RC_SUCCESS;
+  lgs_ckpt_stream_cfg_v3_t *ckpt_stream_cfg_msg_ptr = NULL, 
**ckpt_stream_cfg_msg_dec_ptr;
+
+  EDU_INST_SET ckpt_stream_cfg_rec_ed_rules[] = {
+    {EDU_START, edp_ed_cfg_stream_rec_v6, 0, 0, 0, 
sizeof(lgs_ckpt_stream_cfg_v3_t), 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->name, 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->fileName, 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->pathName, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->maxLogFileSize, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->fixedLogRecordSize, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->haProperty, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->logFullAction, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->logFullHaltThreshold, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->maxFilesRotated, 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->logFileFormat, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns32, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->severityFilter, 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->logFileCurrent, 0, NULL},
+    {EDU_EXEC, ncs_edp_string, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->dest_names, 0, NULL},
+    {EDU_EXEC, ncs_edp_uns64, 0, 0, 0, (long)&((lgs_ckpt_stream_cfg_v3_t 
*)0)->c_file_close_time_stamp, 0, NULL},
+    {EDU_END, 0, 0, 0, 0, 0, 0, NULL},
+  };
+
+  if (op == EDP_OP_TYPE_ENC) {
+    ckpt_stream_cfg_msg_ptr = static_cast<lgs_ckpt_stream_cfg_v3_t *>(ptr);
+  } else if (op == EDP_OP_TYPE_DEC) {
+    ckpt_stream_cfg_msg_dec_ptr = static_cast<lgs_ckpt_stream_cfg_v3_t 
**>(ptr);
+    if (*ckpt_stream_cfg_msg_dec_ptr == NULL) {
+      *o_err = EDU_ERR_MEM_FAIL;
+      return NCSCC_RC_FAILURE;
+    }
+    memset(*ckpt_stream_cfg_msg_dec_ptr, '\0', 
sizeof(lgs_ckpt_stream_cfg_v3_t));
+    ckpt_stream_cfg_msg_ptr = *ckpt_stream_cfg_msg_dec_ptr;
+  } else {
+    ckpt_stream_cfg_msg_ptr = static_cast<lgs_ckpt_stream_cfg_v3_t *>(ptr);
+  }
+
+  rc = m_NCS_EDU_RUN_RULES(edu_hdl, edu_tkn,
+                           ckpt_stream_cfg_rec_ed_rules,
+                           ckpt_stream_cfg_msg_ptr,
+                           ptr_data_len,
+                           buf_env, op, o_err);
+  return rc;
+}
+
+/****************************************************************************
  * Name          : edp_ed_ckpt_msg_v6
  *
  * Description   : This function is an EDU program for encoding/decoding
@@ -137,7 +203,7 @@ uint32_t edp_ed_ckpt_msg_v6(EDU_HDL *edu
      (long)&((lgsv_ckpt_msg_v6_t *)0)->ckpt_rec.stream_cfg, 0, NULL},
 
     /* Cfg stream */
-    {EDU_EXEC, edp_ed_cfg_stream_rec_v2, 0, 0, static_cast<int>(EDU_EXIT),
+    {EDU_EXEC, edp_ed_cfg_stream_rec_v6, 0, 0, static_cast<int>(EDU_EXIT),
      (long)&((lgsv_ckpt_msg_v6_t *)0)->ckpt_rec.stream_cfg, 0, NULL},
 
     /* Lgs cfg */
@@ -166,4 +232,4 @@ uint32_t edp_ed_ckpt_msg_v6(EDU_HDL *edu
 
   return rc;
 
-}       /* End edu_enc_dec_ckpt_msg() */
+}
diff --git a/src/log/logd/lgs_mbcsv_v6.h b/src/log/logd/lgs_mbcsv_v6.h
--- a/src/log/logd/lgs_mbcsv_v6.h
+++ b/src/log/logd/lgs_mbcsv_v6.h
@@ -1,6 +1,5 @@
 /*      -*- OpenSAF  -*-
  *
- * (C) Copyright 2017 The OpenSAF Foundation
  * Copyright Ericsson AB 2017 - All Rights Reserved.
  *
  * This program is distributed in the hope that it will be useful, but
@@ -24,6 +23,23 @@
 #include "lgs_mbcsv_v2.h"
 #include "lgs_mbcsv_v5.h"
 
+typedef struct {
+  char *name;
+  char *fileName;
+  char *pathName;
+  SaUint64T maxLogFileSize;
+  SaUint32T fixedLogRecordSize;
+  SaBoolT haProperty;     /* app log stream only */
+  SaLogFileFullActionT logFullAction;
+  SaUint32T logFullHaltThreshold; /* !app log stream */
+  SaUint32T maxFilesRotated;
+  char *logFileFormat;
+  SaUint32T severityFilter;
+  char *logFileCurrent;
+  char *dest_names;
+  uint64_t c_file_close_time_stamp; /* Time in sec for file rename on Active */
+} lgs_ckpt_stream_cfg_v3_t;
+
 /* Initialize checkpoint record, used in cold/async checkpoint updates */
 typedef struct {
   uint32_t client_id;     /* Client Id at Active */
@@ -41,16 +57,20 @@ typedef struct {
     lgs_ckpt_agent_down_v2_t agent_down;
     lgs_ckpt_stream_open_t stream_open;
     lgs_ckpt_stream_close_v2_t stream_close;
-    lgs_ckpt_stream_cfg_v2_t stream_cfg;
+    lgs_ckpt_stream_cfg_v3_t stream_cfg;
     lgs_ckpt_lgs_cfg_v5_t lgs_cfg;
   } ckpt_rec;
 } lgsv_ckpt_msg_v6_t;
 
+uint32_t edp_ed_cfg_stream_rec_v6(EDU_HDL *edu_hdl, EDU_TKN *edu_tkn,
+                                  NCSCONTEXT ptr, uint32_t *ptr_data_len,
+                                  EDU_BUF_ENV *buf_env, EDP_OP_TYPE op, 
EDU_ERR *o_err);
+uint32_t edp_ed_ckpt_msg_v6(EDU_HDL *edu_hdl, EDU_TKN *edu_tkn,
+                            NCSCONTEXT ptr, uint32_t *ptr_data_len,
+                            EDU_BUF_ENV *buf_env, EDP_OP_TYPE op, EDU_ERR 
*o_err);
+
 uint32_t edp_ed_reg_rec_v6(EDU_HDL *edu_hdl, EDU_TKN *edu_tkn,
                            NCSCONTEXT ptr, uint32_t *ptr_data_len,
                            EDU_BUF_ENV *buf_env, EDP_OP_TYPE op, EDU_ERR 
*o_err);
-uint32_t edp_ed_ckpt_msg_v6(EDU_HDL *edu_hdl, EDU_TKN *edu_tkn,
-                            NCSCONTEXT ptr, uint32_t *ptr_data_len, 
EDU_BUF_ENV *buf_env,
-                            EDP_OP_TYPE op, EDU_ERR *o_err);
 
 #endif  // LOG_LOGD_LGS_MBCSV_V6_H_
diff --git a/src/log/logd/lgs_nildest.cc b/src/log/logd/lgs_nildest.cc
new file mode 100644
--- /dev/null
+++ b/src/log/logd/lgs_nildest.cc
@@ -0,0 +1,153 @@
+/*      -*- OpenSAF  -*-
+ *
+ * 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 "log/logd/lgs_nildest.h"
+
+#include <algorithm>
+
+#include "base/logtrace.h"
+
+static const char kDelimeter[] = ";";
+
+
+//==============================================================================
+// NilDestType class
+//==============================================================================
+NilDestType NilDestType::me_;
+const char NilDestType::name_[] = "NILDEST";
+
+NilDestType::NilDestType() {}
+
+NilDestType::~NilDestType() {
+}
+
+const VectorString NilDestType::GetAllDestStatus() {
+  VectorString output{};
+  if (me_.destnames_.size() == 0) return output;
+  for (const auto& it : me_.destnames_) {
+    std::string status = it + "," + "NOT_CONNECTED";
+    TRACE("%s status = %s", __func__, status.c_str());
+    output.push_back(status);
+  }
+  return output;
+}
+
+bool NilDestType::FindName(const std::string& name) const {
+  return (std::find(destnames_.begin(), destnames_.end(), name) !=
+          destnames_.end());
+}
+
+ErrCode NilDestType::HandleRecordMsg(
+    const DestinationHandler::RecordMsg& msg) {
+  ErrCode ret = ErrCode::kOk;
+  TRACE("Destination name(%s) is NILDEST. Drop msg!", msg.name);
+  ret = ErrCode::kDrop;
+  return ret;
+}
+
+ErrCode NilDestType::HandleCfgMsg(
+    const DestinationHandler::CfgDestMsg& msg) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  const std::string name{msg.name};
+  TRACE("%s Add NILDEST with name (%s)", __func__, msg.name);
+  destnames_.push_back(name);
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode NilDestType::HandleDelCfgMsg(
+    const DestinationHandler::DelDestMsg& msg) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  TRACE("%s remove NILDEST destination name (%s)", __func__, msg.name);
+  const std::string name{msg.name};
+  destnames_.erase(std::remove(destnames_.begin(),
+                               destnames_.end(),
+                               name)
+                   , destnames_.end());
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode NilDestType::ProcessMsg(
+    const DestinationHandler::HandleMsg& msg) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  static bool cfgsent = false;
+  // The proper sequence should be:
+  // 1) kCfgDest   : for configure destination handlers
+  // 2) kSendRecord: write log record to destination
+  // 3) kDelDest   : delete specific destinations
+  // 4) kNoDest    : delete all destinations
+  // Msg could be drop if the sequence is not correct.
+  switch (msg.type) {
+    case DestinationHandler::MsgType::kSendRecord: {
+      // No destination yet configured. Drop the message.
+      TRACE("%s %s", __func__, "RecordMsg msg");
+      const DestinationHandler::RecordMsg& rmsg = msg.info.rec;
+
+      // No destination has been configured so far.
+      if (cfgsent == false) {
+        LOG_WA("Destination handler not yet initialized. Drop msg.");
+        ret = ErrCode::kDrop;
+      } else {
+        ret = HandleRecordMsg(rmsg);
+      }
+      break;
+    }
+
+    case DestinationHandler::MsgType::kCfgDest: {
+      const DestinationHandler::CfgDestMsg& cmsg = msg.info.cfg;
+      TRACE("%s %s", __func__, "CfgDestMsg msg");
+      ret = HandleCfgMsg(cmsg);
+      // Notice there at leat one destination configured.
+      if (ret == ErrCode::kOk) cfgsent = true;
+      break;
+    }
+
+    case DestinationHandler::MsgType::kDelDest: {
+      const DestinationHandler::DelDestMsg& dmsg = msg.info.del;
+      TRACE("%s %s", __func__, "DelDestMsg msg");
+      // No destination yet configured. Drop the message.
+      if (cfgsent == false) {
+        LOG_NO("No destination configured yet. Drop msg.");
+        return ErrCode::kDrop;
+      }
+      ret = HandleDelCfgMsg(dmsg);
+      break;
+    }
+
+    case DestinationHandler::MsgType::kNoDest: {
+      LOG_NO("%s %s", __func__, "DelAllCfgDest msg");
+      if (cfgsent == false) {
+        LOG_NO("No destination configured yet");
+        return ErrCode::kDrop;
+      }
+      destnames_.clear();
+      cfgsent = false;
+      break;
+    }
+
+    default:
+      LOG_ER("Unknown dispatch message type %d", static_cast<int>(msg.type));
+      ret = ErrCode::kInvalid;
+  }
+
+  TRACE_LEAVE();
+  return ret;
+}
diff --git a/src/log/logd/lgs_nildest.h b/src/log/logd/lgs_nildest.h
new file mode 100644
--- /dev/null
+++ b/src/log/logd/lgs_nildest.h
@@ -0,0 +1,72 @@
+/*      -*- OpenSAF  -*-
+ *
+ * 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 SRC_LOG_LOGD_LGS_NILDEST_H_
+#define SRC_LOG_LOGD_LGS_NILDEST_H_
+
+#include <string>
+
+#include "log/logd/lgs_common.h"
+#include "log/logd/lgs_dest.h"
+
+//>
+// @NilDestType - represent NILDEST destination type
+//
+//<
+class NilDestType {
+ public:
+  // @NilDestType class is dedicated to @DestinationHandler class.
+  // Outside world is not granted to access to this class resources
+  friend class DestinationHandler;
+
+  // Get @type that represents for @NilDestType. This type must be unique.
+  static const std::string Type() { return std::string{name_}; }
+
+ private:
+  NilDestType();
+  ~NilDestType();
+
+  // Find if any destination @name configured so far
+  bool FindName(const std::string&) const;
+  // Interpret the DestinationHandler msg and forward to right handle
+  ErrCode ProcessMsg(const DestinationHandler::HandleMsg&);
+  // The msg will be droped if sending to @NILDEST type
+  ErrCode HandleRecordMsg(const DestinationHandler::RecordMsg&);
+  // Put the destination name to @destnames_ for querying destination status
+  ErrCode HandleCfgMsg(const DestinationHandler::CfgDestMsg&);
+  // Remove from @destnames_
+  ErrCode HandleDelCfgMsg(const DestinationHandler::DelDestMsg&);
+  // Get all destination status for @NILDEST type
+  // @return a vector of status, with format
+  // {"name,NOT_CONNECTED", "name2,NOT_CONNECTED, etc."}
+  static const VectorString GetAllDestStatus();
+  // Unique object instance for this @NilDestType class
+  static NilDestType& Instance() { return me_; }
+  // Unique instance for this class
+  static NilDestType me_;
+  // The type name of this @NilDestType (my identity).
+  // Must use this "type" name in destination configuration
+  // if want to destinate to @NilDestType
+  static const char name_[];
+
+  // Hold all destination names having @NILDEST type
+  VectorString destnames_;
+
+  DELETE_COPY_AND_MOVE_OPERATORS(NilDestType);
+};
+
+#endif  // SRC_LOG_LOGD_LGS_NILDEST_H_
diff --git a/src/log/logd/lgs_stream.cc b/src/log/logd/lgs_stream.cc
--- a/src/log/logd/lgs_stream.cc
+++ b/src/log/logd/lgs_stream.cc
@@ -27,6 +27,9 @@
  * Examples can be found in this file, e.g. function fileopen(...) below
  */
 
+#include "log/logd/lgs_stream.h"
+#include <algorithm>
+
 #include "log/logd/lgs.h"
 #include "lgs_config.h"
 #include "log/logd/lgs_file.h"
@@ -376,6 +379,8 @@ void log_stream_print(log_stream_t *stre
   TRACE_2("  logRecordId:          %u", stream->logRecordId);
   TRACE_2("  streamType:           %u", stream->streamType);
   TRACE_2("  filtered:             %llu", stream->filtered);
+  TRACE_2("  stb_dest_names:       %s", stream->stb_dest_names.c_str());
+  TRACE_2("  isRtStream:           %d", stream->isRtStream);
 }
 
 /**
@@ -480,7 +485,6 @@ int lgs_populate_log_stream(
   o_stream->logRecordId = logRecordId;
   o_stream->stb_logRecordId = 0;
   o_stream->isRtStream = SA_TRUE;
-
   o_stream->logFileFormat = strdup(logFileFormat);
   if (o_stream->logFileFormat == NULL) {
     LOG_WA("Failed to allocate memory for logFileFormat");
@@ -654,6 +658,7 @@ log_stream_t *log_stream_new(const std::
   stream->creationTimeStamp = lgs_get_SaTime();
   stream->severityFilter = 0x7f;  /* by default all levels are allowed */
   stream->isRtStream = SA_FALSE;
+  stream->dest_names.clear();
 
   /* Initiate local or shared stream file descriptor dependant on shared or
    * split file system
@@ -1492,3 +1497,58 @@ bool check_max_stream() {
   return (numb_of_streams < stream_array_size ? false:true);
 }
 
+void log_stream_add_dest_name(log_stream_t *stream,
+                              const std::vector<std::string>& names) {
+  osafassert(stream != nullptr);
+  for (const auto& it : names) {
+    stream->dest_names.push_back(it);
+  }
+
+  // Prepare stb_dest_names for checkingpoint to standby
+  log_stream_form_dest_names(stream);
+}
+
+void log_stream_replace_dest_name(log_stream_t *stream,
+                                  const std::vector<std::string>& names) {
+  osafassert(stream != nullptr);
+  stream->dest_names = names;
+  // Prepare stb_dest_names for checkingpoint to standby
+  log_stream_form_dest_names(stream);
+}
+
+void log_stream_delete_dest_name(log_stream_t *stream,
+                                 const std::vector<std::string>& names) {
+  osafassert(stream != nullptr);
+  if (names.size() != 0) {
+    for (const auto& it : names) {
+      // Remove deleted destination from internal database
+      stream->dest_names.erase(std::remove(stream->dest_names.begin(),
+                                           stream->dest_names.end(), it),
+                               stream->dest_names.end());
+    }
+  } else {
+    // Delete all destination names
+    stream->dest_names.clear();
+  }
+
+  // Prepare stb_dest_names for checkingpoint to standby
+  log_stream_form_dest_names(stream);
+}
+
+void log_stream_form_dest_names(log_stream_t* stream) {
+  osafassert(stream != nullptr);
+  std::string output{""};
+  bool addSemicolon = false;
+  // Form stb_dest_names before checking point to standby
+  // under format "name1;name2;etc".
+  for (const auto& it : stream->dest_names) {
+    output += it + ";";
+    addSemicolon = true;
+  }
+  if (addSemicolon == true) {
+    osafassert(output.length() > 0);
+    // Remove last semicolon(;)
+    output[output.length() - 1] = '\0';
+  }
+  stream->stb_dest_names = output;
+}
diff --git a/src/log/logd/lgs_stream.h b/src/log/logd/lgs_stream.h
--- a/src/log/logd/lgs_stream.h
+++ b/src/log/logd/lgs_stream.h
@@ -1,6 +1,7 @@
 /*      -*- OpenSAF  -*-
  *
  * (C) Copyright 2008 The OpenSAF Foundation
+ * Copyright Ericsson AB 2008, 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
@@ -22,6 +23,7 @@
 #include "base/ncspatricia.h"
 #include <time.h>
 #include <limits.h>
+#include <vector>
 
 #include "lgs_fmt.h"
 #include "base/osaf_extended_name.h"
@@ -83,6 +85,12 @@ typedef struct log_stream {
   std::string stb_logFileCurrent; /* Current file name used on standby */
   std::string stb_prev_actlogFileCurrent; /* current file name on active when 
previous record was written */
   uint32_t stb_curFileSize;       /* Bytes written to current log file */
+
+  // Hold vector of destname string {"name1", "name2", etc.}
+  std::vector<std::string> dest_names;
+  // Hold a list of strings separated by semicolon "name1;name2;etc"
+  // This data is used to checkpoint to standby
+  std::string stb_dest_names;
 } log_stream_t;
 
 extern uint32_t log_stream_init();
@@ -133,4 +141,12 @@ void log_free_stream_resources(log_strea
 log_stream_t *iterate_all_streams(SaBoolT &end, SaBoolT jstart);
 extern log_stream_t *log_stream_get_by_name(const std::string &name);
 
+void log_stream_add_dest_name(log_stream_t *stream,
+                              const std::vector<std::string>& names);
+void log_stream_replace_dest_name(log_stream_t *stream,
+                                  const std::vector<std::string>& names);
+void log_stream_delete_dest_name(log_stream_t *stream,
+                                 const std::vector<std::string>& names);
+void log_stream_form_dest_names(log_stream_t* stream);
+
 #endif  // LOG_LOGD_LGS_STREAM_H_
diff --git a/src/log/logd/lgs_unixsock_dest.cc 
b/src/log/logd/lgs_unixsock_dest.cc
new file mode 100644
--- /dev/null
+++ b/src/log/logd/lgs_unixsock_dest.cc
@@ -0,0 +1,343 @@
+/*      -*- OpenSAF  -*-
+ *
+ * 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 "log/logd/lgs_unixsock_dest.h"
+
+#include <algorithm>
+
+#include "log/logd/lgs_util.h"
+#include "log/logd/lgs_nildest.h"
+#include "base/logtrace.h"
+
+// RFC5424 syslog msg format
+// =========================
+// 1) HEADER
+// PRI             = 16*8 + SaLogSeverityT
+// VERSION         = 1
+// TIMESTAMP       = When a log record was actually logged
+// E.g: 2016-12-02T11:34:15.944364+01:00
+// HOSTNAME        = FQDN
+// APP-NAME        = App name who generated log record
+// PROCID          = NILVALUE
+// MSGID           = A hash generated from log stream DN
+// STRUCTURED-DATA = NILVALUE
+//
+// 2) MSG BODY
+// MSG             = The log record written to local file
+//
+
+static const char kDelimeter[] = ";";
+
+//==============================================================================
+// UnixSocketHandler class
+//==============================================================================
+UnixSocketHandler::UnixSocketHandler(const char* socket_name)
+    : sock_path_{socket_name}
+    , sock_{socket_name}
+    , status_{DestinationStatus::kFailed} {
+      // Open the unix socket & and flush the destination status to @status_
+      Open();
+};
+
+void UnixSocketHandler::FlushStatus() {
+  if (sock_.fd() >= 0) {
+    status_ = DestinationStatus::kActive;
+  } else {
+    status_ = DestinationStatus::kFailed;
+  }
+}
+
+DestinationStatus UnixSocketHandler::GetSockStatus() {
+  Open();
+  return status_;
+}
+
+void UnixSocketHandler::Open() {
+  FlushStatus();
+}
+
+void UnixSocketHandler::FormRfc5424(
+    const DestinationHandler::RecordInfo& msg,
+    RfcBuffer* buf) {
+  base::LogMessage::Severity sev{Sev(msg.severity)};
+  base::LogMessage::HostName hostname{msg.origin};
+  base::LogMessage::ProcId procid{""};
+  base::LogMessage::AppName appname{msg.app_name};
+
+  base::LogMessage::Write(base::LogMessage::Facility::kLocal0,
+                          sev,
+                          msg.time,
+                          hostname,
+                          appname,
+                          procid,
+                          base::LogMessage::MsgId{msg.msgid},
+                          {},
+                          std::string{msg.log_record},
+                          buf);
+}
+
+ErrCode UnixSocketHandler::Send(const DestinationHandler::RecordInfo& msg) {
+  TRACE_ENTER();
+  RfcBuffer buffer;
+
+  FormRfc5424(msg, &buffer);
+
+  ssize_t length = buffer.size();
+  ssize_t len = sock_.Send(buffer.data(), length);
+  // Resend as probably receiver has just been restarted.
+  // Retry only once, if not succesfully, leave it to upper layer decision.
+  if (len != length) {
+    LOG_NO("Failed to send log record to socket destination. Resent.");
+    len = sock_.Send(buffer.data(), length);
+  }
+
+  FlushStatus();
+
+  TRACE_LEAVE();
+  return ((len != length) ? (ErrCode::kErr) : (ErrCode::kOk));
+}
+
+void UnixSocketHandler::Close() {
+  // Do nothing
+}
+
+// Convert AIS sev @a sev to Severity class
+base::LogMessage::Severity UnixSocketHandler::Sev(uint16_t sev) {
+  switch (sev) {
+    case SA_LOG_SEV_EMERGENCY:
+      return base::LogMessage::Severity::kEmerg;
+    case SA_LOG_SEV_ALERT:
+      return base::LogMessage::Severity::kAlert;
+    case SA_LOG_SEV_CRITICAL:
+      return base::LogMessage::Severity::kCrit;
+    case SA_LOG_SEV_ERROR:
+      return base::LogMessage::Severity::kErr;
+    case SA_LOG_SEV_WARNING:
+      return base::LogMessage::Severity::kWarning;
+    case SA_LOG_SEV_NOTICE:
+      return base::LogMessage::Severity::kNotice;
+    case SA_LOG_SEV_INFO:
+      return base::LogMessage::Severity::kInfo;
+    default:
+      LOG_ER("Unknown severity level (%d)", (int)sev);
+      return base::LogMessage::Severity::kErr;
+  }
+}
+
+UnixSocketHandler::~UnixSocketHandler() {
+  // Destination is deleted
+  status_ = DestinationStatus::kFailed;
+  // The parent class will do closing the connection
+  // and other resources.
+}
+
+//==============================================================================
+// UnixSocketType class
+//==============================================================================
+UnixSocketType UnixSocketType::me_;
+const char UnixSocketType::name_[] = "UNIX_SOCKET";
+
+UnixSocketType::UnixSocketType() {}
+
+UnixSocketType::~UnixSocketType() {
+  // Close all connections & stop the thread
+}
+
+const std::string UnixSocketType::DestStatusToStr(DestinationStatus status) {
+  switch (status) {
+    // Destination configured and is being connected to destination
+    case DestinationStatus::kActive: return "CONNECTED";
+      // Destination configured and is being disconnected to destination
+    case DestinationStatus::kFailed: return "FAILED";
+    default: return "UNKNOWN";
+  }
+}
+
+const std::string UnixSocketType::GetDestinationStatus(
+    const std::string& name) {
+  if (FindName(name) == false) return std::string{"UNKNOWN"};
+  const UnixSocketHandler* sk = name_sockethdlr_map_[name];
+  if (sk == nullptr) return std::string{"FAILED"};
+  return DestStatusToStr(name_sockethdlr_map_[name]->GetSockStatus());
+}
+
+const VectorString UnixSocketType::GetAllDestStatus() {
+  VectorString output{};
+  if (me_.name_sockethdlr_map_.size() == 0) return output;
+  for (const auto& it : me_.name_sockethdlr_map_) {
+    std::string status = it.first + "," + me_.GetDestinationStatus(it.first);
+    TRACE("%s status = %s", __func__, status.c_str());
+    output.push_back(status);
+  }
+  return output;
+}
+
+bool UnixSocketType::FindName(const std::string& name) const {
+  return ((name_sockethdlr_map_.find(name) == name_sockethdlr_map_.end()) ?
+          (false) : (true));
+}
+
+UnixSocketHandler* UnixSocketType::GetDestHandle(const std::string& name) {
+  if (FindName(name) == false) return nullptr;
+  return name_sockethdlr_map_[name];
+}
+
+const char* UnixSocketType::GetCfgValue(const std::string& name) {
+  if (FindName(name) == false) return nullptr;
+  UnixSocketHandler* sk = name_sockethdlr_map_[name];
+  return (sk == nullptr) ? (nullptr) :
+      (name_sockethdlr_map_[name]->sock_path_.c_str());
+}
+
+ErrCode UnixSocketType::HandleRecordMsg(
+    const DestinationHandler::RecordMsg& msg) {
+  ErrCode ret = ErrCode::kOk;
+  // No destination configuration with this destination name
+  if (FindName(std::string{msg.name}) == false) {
+    LOG_WA("No such destination name(%s) configured", msg.name);
+    ret = ErrCode::kDrop;
+    return ret;
+  }
+
+  // NIL destination - only have "name" and "type", but no "value".
+  if (GetDestHandle(std::string{msg.name}) == nullptr) {
+    LOG_NO("The destination name(%s) not yet configured", msg.name);
+    ret = ErrCode::kDrop;
+    return ret;
+  }
+
+  // Send to destination end.
+  osafassert(name_sockethdlr_map_[std::string{msg.name}] != nullptr);
+  return name_sockethdlr_map_[std::string{msg.name}]->Send(msg.rec);
+}
+
+ErrCode UnixSocketType::HandleCfgMsg(
+    const DestinationHandler::CfgDestMsg& msg) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  const std::string name{msg.name};
+  const char* newcfg = msg.value;
+
+  // Close and free previous @LocakSkDestHanlde instance if existing.
+  if (GetDestHandle(name) != nullptr) {
+    TRACE("%s delete old destination value for name (%s)", __func__, msg.name);
+    delete name_sockethdlr_map_[name];
+    name_sockethdlr_map_[name] = nullptr;
+  }
+
+  // Request to create destination handler
+  if ((newcfg != nullptr) && (strlen(newcfg) > 0)) {
+    TRACE("%s add new destination name (%s)", __func__, msg.name);
+    // Create a new destination handler @UnixSocketHandler for @name
+    name_sockethdlr_map_[name] = new UnixSocketHandler(newcfg);
+    ret = (name_sockethdlr_map_[name] == nullptr) ? ErrCode::kNoMem : ret;
+    return ret;
+  } else {
+    TRACE("%s create nildest with destination name (%s)", __func__, msg.name);
+    // nildest case with format "name;type;"
+    name_sockethdlr_map_[name] = nullptr;
+  }
+
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode UnixSocketType::HandleDelCfgMsg(
+    const DestinationHandler::DelDestMsg& msg) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  TRACE("%s remove destination name (%s)", __func__, msg.name);
+  const std::string name{msg.name};
+  // Close and free @LocakSkDestHanlde instance if existing.
+  if (GetDestHandle(name) != nullptr) delete name_sockethdlr_map_[name];
+  // Erase from the map
+  name_sockethdlr_map_.erase(name);
+  TRACE_LEAVE();
+  return ret;
+}
+
+ErrCode UnixSocketType::ProcessMsg(
+    const DestinationHandler::HandleMsg& msg) {
+  TRACE_ENTER();
+  ErrCode ret = ErrCode::kOk;
+  static bool cfgsent = false;
+  // The proper sequence should be:
+  // 1) kCfgDest   : for configure destination handlers
+  // 2) kSendRecord: write log record to destination
+  // 3) kDelDest   : delete specific destinations
+  // 4) kNoDest    : delete all destinations
+  // Msg could be drop if the sequence is not correct.
+  switch (msg.type) {
+    case DestinationHandler::MsgType::kSendRecord: {
+      // No destination yet configured. Drop the message.
+      TRACE("%s %s", __func__, "RecordMsg msg");
+      const DestinationHandler::RecordMsg& rmsg = msg.info.rec;
+
+      // No destination has been configured so far.
+      if (cfgsent == false) {
+        LOG_WA("Destination handler not yet initialized. Drop msg.");
+        ret = ErrCode::kDrop;
+      } else {
+        ret = HandleRecordMsg(rmsg);
+      }
+      break;
+    }
+
+    case DestinationHandler::MsgType::kCfgDest: {
+      const DestinationHandler::CfgDestMsg& cmsg = msg.info.cfg;
+      TRACE("%s %s", __func__, "CfgDestMsg msg");
+      ret = HandleCfgMsg(cmsg);
+      // Notice there at leat one destination configured.
+      if (ret == ErrCode::kOk) cfgsent = true;
+      break;
+    }
+
+    case DestinationHandler::MsgType::kDelDest: {
+      const DestinationHandler::DelDestMsg& dmsg = msg.info.del;
+      TRACE("%s %s", __func__, "DelDestMsg msg");
+      // No destination yet configured. Drop the message.
+      if (cfgsent == false) {
+        LOG_NO("No destination configured yet. Drop msg.");
+        return ErrCode::kDrop;
+      }
+      ret = HandleDelCfgMsg(dmsg);
+      break;
+    }
+
+    case DestinationHandler::MsgType::kNoDest: {
+      LOG_NO("%s %s", __func__, "DelAllCfgDest msg");
+      if (cfgsent == false) {
+        LOG_NO("No destination configured yet");
+        return ErrCode::kDrop;
+      }
+
+      for (auto& it : name_sockethdlr_map_) {
+        if (it.second != nullptr) delete it.second;
+        name_sockethdlr_map_.erase(it.first);
+      }
+      cfgsent = false;
+      break;
+    }
+
+    default:
+      LOG_ER("Unknown dispatch message type %d", static_cast<int>(msg.type));
+      ret = ErrCode::kInvalid;
+  }
+
+  TRACE_LEAVE();
+  return ret;
+}
diff --git a/src/log/logd/lgs_unixsock_dest.h b/src/log/logd/lgs_unixsock_dest.h
new file mode 100644
--- /dev/null
+++ b/src/log/logd/lgs_unixsock_dest.h
@@ -0,0 +1,245 @@
+/*      -*- OpenSAF  -*-
+ *
+ * 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 SRC_LOG_LOGD_LGS_UNIXSOCK_DEST_H_
+#define SRC_LOG_LOGD_LGS_UNIXSOCK_DEST_H_
+
+#include <string>
+#include <map>
+
+#include "base/log_message.h"
+#include "base/time.h"
+#include "base/unix_client_socket.h"
+#include "log/logd/lgs_common.h"
+#include "log/logd/lgs_dest.h"
+
+//>
+// Represent connection to local Unix Domain socket
+// @UnixSocketHandler = Local Socket Destination class
+//
+// Each destination name will have its own instance
+// of this class. It represents the connection
+// to the destination. So, it contains connection status,
+// and the sequence message ID @msg_id_, showing how many logrecord
+// has been written to the socket. This sequence number
+// helps to detect if there is any msg lost so far.
+//
+// When getting a send log record request, this class
+// will form message complying with RFC5424 protocol, putting
+// the log record which has been writen to local file at MSG field,
+// also do fill information to other fields in protocol header.
+//
+// The instance of this class is only deleted if destination info (@value)
+// is deleted from Destination Configuration attribute.
+//
+// It would be wrong if more than one instance of this class
+// pointed to same Domain Unix Socket (same local path name).
+//
+// Regarding connection status, the status will be flushed to
+// latest one at points:
+// 1) Creating new destination:
+//    The instance will open a connection to destination,
+//    then update its status based on the file descriptor.
+//
+// 2) Change destination to new one:
+//    The previous connection is closed and opened a new one
+//    toward the destination, then update the status
+//    based on the file descriptor.
+//
+// 3) When writing log record to destination:
+//    This class is not aware of the destination whether
+//    the destination is dead or restarted.
+//    If it gets failed to write to socket, the status will be updated.
+//    just do that to detect the receiver on destination is dead/restarted.
+//
+// 4) Delete the destination
+//    When deleing destination, the instance will be deleted.
+//    Then, the status will be updated accordingly.
+//
+// All methods provided by this class should grant access
+// to @UnixSocketType only. No one else can directly
+// invoke any method of this class.
+//
+// With @Send(), if the msg get failed to send to destination,
+// will log info to syslog and return the error code to upper layer.
+//
+// The other information on this class is that, within @Send()
+// method, creating a very big stack buffer - over 65*1024 bytes
+// even the sent RFC msg is quite short (e.g: rfc header + 150 bytes).
+//<
+class UnixSocketHandler {
+ public:
+  // Set stack size of buffer to maximum size.
+  static const uint32_t kBufMaxSize = 65*1024 + 1024;
+  // typedef, aim to shorten declaration.
+  using RfcBuffer = base::Buffer<kBufMaxSize>;
+
+  // This class is only dedicated to UnixSocketType.
+  friend class UnixSocketType;
+  // Open and connect to the socket
+  void Open();
+  // @Close Do nothing
+  void Close();
+  // Form RFC5424 and send to the socket
+  ErrCode Send(const DestinationHandler::RecordInfo&);
+  // Get the socket status
+  DestinationStatus GetSockStatus();
+  // Form rfc5424 syslog format
+  static void FormRfc5424(
+      const DestinationHandler::RecordInfo& msg,
+      RfcBuffer* buf);
+
+ private:
+  explicit UnixSocketHandler(const char*);
+  ~UnixSocketHandler();
+
+  // Get the @status_ up-to-date
+  void FlushStatus();
+  // Convert AIS log stream severity to syslog severity
+  static base::LogMessage::Severity Sev(uint16_t);
+
+  // Hold destination info (@value)
+  std::string sock_path_;
+  base::UnixClientSocket sock_;
+  // Hold the connection status
+  DestinationStatus status_;
+
+  DELETE_COPY_AND_MOVE_OPERATORS(UnixSocketHandler);
+};
+
+//>
+// @UnixSocketType: Local Socket Destination Type
+//
+// This class represents local socket destination type.
+// Every destinations name with local socket destination type
+// must refer to one and only one instance of this class.
+// Therefore, it has been designed as a singleton pattern.
+//
+// Outside world are not allowed to see any methods of this class
+// except DestinationHandler. Right after analyzing DestinationHandler 
messages,
+// knowing what is the destination type, DestinationHandler will pass
+// the message to this class via the unique instance.
+//
+// This class holds a map b/w destination name and @UnixSocketHandler instance
+// that represents the connection to destination for that destination name.
+//
+// Based on what type of messages come to, it will take specific actions
+// accordingly.
+// 1) Create/Update destination message
+//    The message will contain destination name, destination type,
+//    and probably destination info (local socket path).
+//    Basing on the destination name, the Handler will sure that
+//    a) Any such destination name has been created before?
+//    b) And any @UnixSocketHandler instance is mapped to that
+//       destination name?
+//    If no destination name exist in the @map, then Hander will
+//    create a new pair <name, @UnixSocketHandler> and update its destination.
+//    If the destination name already exist, will check if configuration info
+//    provided in the message is different with the existing or not.
+//    We expect it should be different with the existing, otherwise
+//    we have code fault somewhere. If different from the existing, closing
+//    the old connection, and creating new one and re-map to that name.
+//
+// 2) Sending log record message
+//    The message will contain destination name, log record, log stream DN
+//    and other infos. Basing on the destination name, the Handler will sure:
+//    a) Any such destination name has been created before?
+//    b) And any @UnixSocketHandler instance is mapped to that
+//       destination name?
+//    With this message, the Handler expects the destination name and
+//    @UnixSocketHandler must exist. Otherwise, there is a code
+//    fault somewhere.
+//    and the Handler will drop that message and log info to syslog.
+//    If as expected, the Handler will send the message to
+//    @UnixSocketHandler instance, in turn,
+//    it will form that message according to RF5424 format and
+//    send that message to local unix socket.
+//
+// 3) Delete destination message
+//    This message will contain only destination name. Basing on that info,
+//    the Hanlder will find that name in the @map to see any name is there.
+//    If have, do:
+//    1) Close the connection
+//    2) Free resources connected to that destination name
+//    Once destination name is deleted, any log record sent to destination name
+//    will be drop or no writing to the destination happens.
+//<
+class UnixSocketType {
+ public:
+  // @UnixSocketType class is dedicated to @DestinationHandler class.
+  // Outside world is not granted to access to this class resources
+  friend class DestinationHandler;
+
+  // Get @type that represents for @UnixSocketType. This type must be unique.
+  static const std::string Type() { return std::string{name_}; }
+
+ private:
+  // typedef for the map. Aim to shorten the type declaration.
+  using NameSockHdlrMap =  std::map<std::string, UnixSocketHandler*>;
+
+  UnixSocketType();
+  ~UnixSocketType();
+
+  // Get @UnixSocketHandler instance which has been assigned
+  // to this destination @name
+  UnixSocketHandler* GetDestHandle(const std::string& name);
+  // Get configuration value that goes with this destination name @name
+  const char* GetCfgValue(const std::string& name);
+  // Find if any destination @name configured so far
+  bool FindName(const std::string&) const;
+  // Interpret the DestinationHandler msg and forward to right handle
+  ErrCode ProcessMsg(const DestinationHandler::HandleMsg&);
+  // Processing @Dispacher::RecordMsg msg, then forward to its instance
+  // @UnixSocketHandler to form RFC5424 syslog format before writing to socket.
+  ErrCode HandleRecordMsg(const DestinationHandler::RecordMsg&);
+  // Processing @DestinationHandler::CfgDestMsg msg. Go thought all 
destinations
+  // and create or update destination configuration depending on contents.
+  ErrCode HandleCfgMsg(const DestinationHandler::CfgDestMsg&);
+  // Processing @DestinationHandler::CfgDestMsg msg. Go thought all deleted
+  // destinations names, then do close connections and free resources.
+  ErrCode HandleDelCfgMsg(const DestinationHandler::DelDestMsg&);
+  // Get destination status for destination @name.
+  // The value could be:
+  // 1) "CONNECTED": destination @name is configured and connected to other 
end.
+  // 2) "FAILED": destination @name is configured, but not able co connect to.
+  //    or no destination value has been given.
+  const std::string GetDestinationStatus(const std::string& name);
+  // Convert enum status to string
+  const std::string DestStatusToStr(DestinationStatus status);
+  // Get the unique instance represents this unique type
+  // This method should only be used by @DestinationHandler class
+  // Get all destination status for Local Socket Destination Typen
+  // @return a vector of status, with format
+  // {"name,active", "name2,inactive, etc."}
+  static const VectorString GetAllDestStatus();
+  // Unique object instance for this @UnixSocketType class
+  static UnixSocketType& Instance() { return me_; }
+  // Unique instance for this class
+  static UnixSocketType me_;
+  // The type name of this @UnixSocketType (my identity).
+  // Must use this "type" name in destination configuration
+  // if want to destinate to local unix socket.
+  static const char name_[];
+
+  // Map b/w dest name and its own @UnixSocketHandler instance.
+  // Same @name must have same @UnixSocketHandler instance.
+  NameSockHdlrMap name_sockethdlr_map_;
+
+  DELETE_COPY_AND_MOVE_OPERATORS(UnixSocketType);
+};
+
+#endif  // SRC_LOG_LOGD_LGS_UNIXSOCK_DEST_H_
diff --git a/src/log/logd/lgs_util.cc b/src/log/logd/lgs_util.cc
--- a/src/log/logd/lgs_util.cc
+++ b/src/log/logd/lgs_util.cc
@@ -837,6 +837,40 @@ bool lgs_is_extended_name_valid(const Sa
   return true;
 }
 
+//==============================================================================
+// logutil namespace
+//==============================================================================
+namespace logutil {
+
+// Split a string @str with delimiter @delimiter
+// and return an vector of strings.
+std::vector<std::string> Parser(const std::string& str,
+                                const std::string& delimiter) {
+  std::vector<std::string> temp;
+  std::string s{str};
+  size_t pos = 0;
+  while ((pos = s.find(delimiter)) != std::string::npos) {
+    temp.push_back(s.substr(0, pos));
+    s.erase(0, pos + delimiter.length());
+  }
+  temp.push_back(s);
+  return temp;
+}
+
+bool isValidName(const std::string& name) {
+  // Valid name if @name not contain any characters outside
+  // of below strings.
+  const std::string validChar = "abcdefghijklmnopqrstuvwxyz"
+                                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                                "01234567890_-";
+  if (name.find_first_not_of(validChar) != std::string::npos)
+    return false;
+
+  return true;
+}
+
+};  // namespace logutil
+
 /**
  * Check if severity filter is supported for client
  *
diff --git a/src/log/logd/lgs_util.h b/src/log/logd/lgs_util.h
--- a/src/log/logd/lgs_util.h
+++ b/src/log/logd/lgs_util.h
@@ -27,7 +27,7 @@
 
 #include "osaf/saf/saAis.h"
 #include "amf/saf/saAmf.h"
-
+#include <vector>
 #include "lgs_stream.h"
 #include "lgs_evt.h"
 
@@ -86,4 +86,13 @@ bool lgs_is_extended_name_valid(const Sa
 void lgs_send_severity_filter_to_clients(uint32_t stream_id,
                                          SaLogSeverityFlagsT serverity);
 
+namespace logutil {
+// Parse string format "a;b;c" to vector of string {a, b, c}
+// In generic, this function could be used to split a string
+// separated by delimiter to a vector of strings.
+std::vector<std::string> Parser(const std::string&, const std::string&);
+// Check if @name contain special characters in.
+bool isValidName(const std::string& name);
+};  // namespace logutil
+
 #endif  // LOG_LOGD_LGS_UTIL_H_

------------------------------------------------------------------------------
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