This is an automated email from the ASF dual-hosted git repository.

vipulrahane pushed a commit to branch vipul/logging_num_entries_callbacks
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit a9a8f5ac4c83940e50149aa70b73a70dc8250a29
Author: Vipul Rahane <[email protected]>
AuthorDate: Mon Mar 18 11:00:56 2024 -0700

    sys/log: Add number of entries support in logs
    
    - Add number of entries support in logs
    - Add TLV for number of entries support
    - Also update log_shell and add syscfgs to support the same.
    - Added support in fcb2 and cbmem for number of entries
    - This makes it such that log entries are backwards and forwards
      compatible.
    - Number of entries can be read using the console or newtmgr
    - This is an optional feature enabled only if
      LOG_FLAGS_TLV_SUPPORT: 1 and LOG_TLV_NUM_ENTRIES: 1
    
    apps/slinky: Dependency issue
    
    - fcb2 and config_fcb2 and log_fcb2 should be selectable via the target
      using syscfgs, it does not need to be specific to the app
    
    sys/log/full/selftest: Add selftests for num_entries
    
    sys/log/full/selftest: Add unit tests for log fcb2 image hash and 
num_entries
    
    sys/log: Update selftests to account for header and trailers
    
    - Earlier these would only account for the entries, now they
      also account for headers and trailers
    
    sys/log: Reverse TLV implementation for number of entries support
    
    callbacks
---
 apps/slinky/pkg.yml                                |  12 -
 fs/fcb2/include/fcb/fcb2.h                         |   9 +
 fs/fcb2/src/fcb.c                                  |   9 +
 fs/fcb2/src/fcb_priv.h                             |   9 -
 hw/mcu/native/src/hal_flash.c                      |   2 +-
 sys/log/common/include/log_common/log_common.h     |  64 +++-
 sys/log/full/include/log/log.h                     | 236 +++++++++++-
 .../selftest/align1_img_hash_num_entries/pkg.yml   |  30 ++
 .../src/log_test_align1.c}                         |  26 +-
 .../align1_img_hash_num_entries/syscfg.yml         |  30 ++
 sys/log/full/selftest/align1_num_entries/pkg.yml   |  30 ++
 .../src/log_test_align1.c}                         |  26 +-
 .../full/selftest/align1_num_entries/syscfg.yml    |  29 ++
 sys/log/full/selftest/align4_num_entries/pkg.yml   |  30 ++
 .../src/log_test_align1.c}                         |  26 +-
 .../src/log_test_align4.c}                         |  28 +-
 .../full/selftest/align4_num_entries/syscfg.yml    |  29 ++
 sys/log/full/selftest/fcb2_align1_imghash/pkg.yml  |  30 ++
 .../src/log_test_align1.c}                         |  26 +-
 .../full/selftest/fcb2_align1_imghash/syscfg.yml   |  29 ++
 .../fcb2_align1_imghash_num_entries/pkg.yml        |  30 ++
 .../src/log_test_align1.c}                         |  26 +-
 .../fcb2_align1_imghash_num_entries/syscfg.yml     |  30 ++
 .../util/include/log_test_util/log_test_util.h     |   4 +
 sys/log/full/selftest/util/src/log_test_util.c     |  91 ++++-
 .../src/testcases/log_test_case_cbmem_append.c     |  25 +-
 .../testcases/log_test_case_cbmem_append_body.c    |  24 +-
 .../testcases/log_test_case_cbmem_append_mbuf.c    |  28 +-
 .../log_test_case_cbmem_append_mbuf_body.c         |  24 +-
 .../src/testcases/log_test_case_cbmem_printf.c     |  23 +-
 .../util/src/testcases/log_test_case_fcb_append.c  |  27 +-
 .../src/testcases/log_test_case_fcb_append_body.c  |  26 +-
 .../src/testcases/log_test_case_fcb_append_mbuf.c  |  26 +-
 .../testcases/log_test_case_fcb_append_mbuf_body.c |  24 +-
 sys/log/full/src/log.c                             | 405 ++++++++++++++++++++-
 sys/log/full/src/log_cbmem.c                       |  74 +++-
 sys/log/full/src/log_fcb.c                         | 210 ++++++++---
 sys/log/full/src/log_fcb2.c                        | 164 +++++++--
 sys/log/full/src/log_shell.c                       |  51 ++-
 sys/log/full/syscfg.yml                            |   6 +
 40 files changed, 1684 insertions(+), 344 deletions(-)

diff --git a/apps/slinky/pkg.yml b/apps/slinky/pkg.yml
index 3c1013123..93db421c4 100644
--- a/apps/slinky/pkg.yml
+++ b/apps/slinky/pkg.yml
@@ -44,15 +44,3 @@ pkg.deps:
 pkg.deps.I2C_0: test/i2c_scan
 pkg.deps.I2C_1: test/i2c_scan
 pkg.deps.I2C_2: test/i2c_scan
-
-pkg.deps.CONFIG_NFFS:
-    - "@apache-mynewt-core/fs/nffs"
-
-pkg.deps.CONFIG_LITTLEFS:
-    - "@apache-mynewt-core/fs/littlefs"
-
-pkg.deps.CONFIG_FCB:
-    - "@apache-mynewt-core/fs/fcb"
-
-pkg.deps.REBOOT_LOG_FCB:
-    - "@apache-mynewt-core/fs/fcb"
diff --git a/fs/fcb2/include/fcb/fcb2.h b/fs/fcb2/include/fcb/fcb2.h
index e089fccbc..2f0c72090 100644
--- a/fs/fcb2/include/fcb/fcb2.h
+++ b/fs/fcb2/include/fcb/fcb2.h
@@ -304,6 +304,15 @@ int fcb2_clear(struct fcb2 *fcb);
  */
 int fcb2_area_info(struct fcb2 *fcb, int sector, int *elemsp, int *bytesp);
 
+/**
+ * Length of data in flash considering alignment
+ *
+ * @param range Active range
+ * @param len Length to be calculated using alignment
+ *
+ */
+int fcb2_len_in_flash(const struct flash_sector_range *range, uint16_t len);
+
 #ifdef __cplusplus
 }
 
diff --git a/fs/fcb2/src/fcb.c b/fs/fcb2/src/fcb.c
index e8a65ff12..0f1e31ee3 100644
--- a/fs/fcb2/src/fcb.c
+++ b/fs/fcb2/src/fcb.c
@@ -101,6 +101,15 @@ fcb2_init(struct fcb2 *fcb)
     return rc;
 }
 
+int
+fcb2_len_in_flash(const struct flash_sector_range *range, uint16_t len)
+{
+    if (range->fsr_align <= 1) {
+        return len;
+    }
+    return (len + (range->fsr_align - 1)) & ~(range->fsr_align - 1);
+}
+
 int
 fcb2_free_sector_cnt(struct fcb2 *fcb)
 {
diff --git a/fs/fcb2/src/fcb_priv.h b/fs/fcb2/src/fcb_priv.h
index d02784fab..6f91ff09f 100644
--- a/fs/fcb2/src/fcb_priv.h
+++ b/fs/fcb2/src/fcb_priv.h
@@ -40,15 +40,6 @@ struct fcb2_sector_info {
     uint16_t si_sector_in_range;          /* Sector number relative to 
si_range */
 };
 
-static inline int
-fcb2_len_in_flash(const struct flash_sector_range *range, uint16_t len)
-{
-    if (range->fsr_align <= 1) {
-        return len;
-    }
-    return (len + (range->fsr_align - 1)) & ~(range->fsr_align - 1);
-}
-
 int fcb2_getnext_in_area(struct fcb2 *fcb, struct fcb2_entry *loc);
 
 static inline int
diff --git a/hw/mcu/native/src/hal_flash.c b/hw/mcu/native/src/hal_flash.c
index b39b5158c..439f9d4fa 100644
--- a/hw/mcu/native/src/hal_flash.c
+++ b/hw/mcu/native/src/hal_flash.c
@@ -152,7 +152,7 @@ static int
 flash_native_write_internal(uint32_t address, const void *src, uint32_t length,
                             int allow_overwrite)
 {
-    static uint8_t buf[256];
+    uint8_t buf[256] = {0};
     uint32_t cur;
     uint32_t end;
     int chunk_sz;
diff --git a/sys/log/common/include/log_common/log_common.h 
b/sys/log/common/include/log_common/log_common.h
index 9974fa2ef..1ec4de551 100644
--- a/sys/log/common/include/log_common/log_common.h
+++ b/sys/log/common/include/log_common/log_common.h
@@ -28,6 +28,7 @@ extern "C" {
 #endif
 
 struct log;
+struct log_entry_hdr;
 
 #define LOG_VERSION_V3  3
 
@@ -132,11 +133,70 @@ typedef void log_append_cb(struct log *log, uint32_t idx);
 
 /** @typdef log_notify_rotate_cb
  * @brief Callback that is executed each time we are about to rotate a log.
- * 
- * @param log                   The log that is about to rotate 
+ *
+ * @param log                   The log that is about to rotate
  */
 typedef void log_notify_rotate_cb(const struct log *log);
 
+/** @typedef log_mbuf_trailer_append_cb
+ * @brief Callback that is executed each time the corresponding log entry is
+ * appended to
+ *
+ * @param log                   The log that was just appended to
+ * @param om                    Pointer to the mbuf that contains the log entry
+ * @param loc                   Argument pointing to the location of
+ *                              the entry
+ * @return                      0 on success, non-zero on failure
+ */
+typedef int log_trailer_mbuf_append_cb(struct log *log, struct os_mbuf *om, 
void *loc);
+
+/** @typedef log_cbmem_trailer_append_cb
+ * @brief Callback that is executed each time the corresponding log entry is
+ * appended to
+ *
+ * @param log                   The log that was just appended to
+ * @param buf                   Pointer to the buffer that contains the log 
entry
+ * @param buflen                Length of the trailer
+ * @return                      0 on success, non-zero on failure
+ */
+typedef int log_cbmem_trailer_append_cb(struct log *log, uint8_t *buf,
+                                        uint16_t buflen);
+
+/** @typedef log_cbmem_trailer_mbuf_append_cb
+ * @brief Callback that is executed each time the corresponding log entry is
+ * appended to
+ *
+ * @param log                   The log that was just appended to
+ * @param om                    Pointer to the mbuf
+ *
+ * @return                      0 on success, non-zero on failure
+ */
+typedef int log_cbmem_trailer_mbuf_append_cb(struct log *log, struct os_mbuf 
*om);
+
+/** @typedef log_trailer_append_cb
+ * @brief Callback that is executed each time the corresponding log entry is
+ * appended to
+ *
+ * @param log                   The log that was just appended to
+ * @param buf                   Buffer to append trailer to
+ * @param buflen                Length of the trailer to be filled up
+ * @param loc                   Argument pointing to the location of
+ *                              the entry
+ * @return                      0 on success, non-zero on failure
+ */
+typedef int log_trailer_append_cb(struct log *log, uint8_t *buf,
+                                  uint16_t buflen, void *loc);
+
+/** @typedef log_trailer_len_cb
+ * @brief Callback used to read length of trailer in a log entry
+ *
+ * @param log                   The log the trailer is to be read from
+ * @param hdr                   Log entry header of the log entry the log is
+ *                              read from
+ * @return                      Length of the appended trailer
+ */
+typedef uint16_t log_trailer_len_cb(struct log *log, const struct 
log_entry_hdr *hdr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/sys/log/full/include/log/log.h b/sys/log/full/include/log/log.h
index d04a191eb..644fc6bea 100644
--- a/sys/log/full/include/log/log.h
+++ b/sys/log/full/include/log/log.h
@@ -77,18 +77,18 @@ struct log_storage_info {
 };
 #endif
 
-typedef int (*log_walk_func_t)(struct log *, struct log_offset *log_offset,
+typedef int (*log_walk_func_t)(struct log *log, struct log_offset *log_offset,
         const void *dptr, uint16_t len);
 
 typedef int (*log_walk_body_func_t)(struct log *log,
         struct log_offset *log_offset, const struct log_entry_hdr *hdr,
         const void *dptr, uint16_t len);
 
-typedef int (*lh_read_func_t)(struct log *, const void *dptr, void *buf,
+typedef int (*lh_read_func_t)(struct log *log, const void *dptr, void *buf,
         uint16_t offset, uint16_t len);
-typedef int (*lh_read_mbuf_func_t)(struct log *, const void *dptr, struct 
os_mbuf *om,
+typedef int (*lh_read_mbuf_func_t)(struct log *log, const void *dptr, struct 
os_mbuf *om,
                                    uint16_t offset, uint16_t len);
-typedef int (*lh_append_func_t)(struct log *, void *buf, int len);
+typedef int (*lh_append_func_t)(struct log *log, void *buf, int len);
 typedef int (*lh_append_body_func_t)(struct log *log,
                                      const struct log_entry_hdr *hdr,
                                      const void *body, int body_len);
@@ -96,16 +96,19 @@ typedef int (*lh_append_mbuf_func_t)(struct log *, struct 
os_mbuf *om);
 typedef int (*lh_append_mbuf_body_func_t)(struct log *log,
                                           const struct log_entry_hdr *hdr,
                                           struct os_mbuf *om);
-typedef int (*lh_walk_func_t)(struct log *,
+typedef int (*lh_walk_func_t)(struct log *log,
         log_walk_func_t walk_func, struct log_offset *log_offset);
-typedef int (*lh_flush_func_t)(struct log *);
+typedef int (*lh_flush_func_t)(struct log *log);
+typedef uint16_t (*lh_read_entry_len_func_t)(struct log *log, const void 
*dptr);
 #if MYNEWT_VAL(LOG_STORAGE_INFO)
-typedef int (*lh_storage_info_func_t)(struct log *, struct log_storage_info *);
+typedef int (*lh_storage_info_func_t)(struct log *log, struct log_storage_info 
*);
 #endif
 #if MYNEWT_VAL(LOG_STORAGE_WATERMARK)
-typedef int (*lh_set_watermark_func_t)(struct log *, uint32_t);
+typedef int (*lh_set_watermark_func_t)(struct log *log, uint32_t);
 #endif
-typedef int (*lh_registered_func_t)(struct log *);
+typedef int (*lh_registered_func_t)(struct log *log);
+/* This calculates length based on alignment of underlying medium */
+typedef int (*lh_len_in_medium_func_t)(struct log *log, uint16_t len);
 
 struct log_handler {
     int log_type;
@@ -118,6 +121,8 @@ struct log_handler {
     lh_walk_func_t log_walk;
     lh_walk_func_t log_walk_sector;
     lh_flush_func_t log_flush;
+    lh_read_entry_len_func_t log_read_entry_len;
+    lh_len_in_medium_func_t log_len_in_medium;
 #if MYNEWT_VAL(LOG_STORAGE_INFO)
     lh_storage_info_func_t log_storage_info;
 #endif
@@ -131,8 +136,12 @@ struct log_handler {
 /* Image hash length to be looged */
 #define LOG_IMG_HASHLEN 4
 
-/* Flags used to indicate type of data in reserved payload*/
-#define LOG_FLAGS_IMG_HASH (1 << 0)
+/* Flags used to indicate type of data in reserved payload */
+#define LOG_FLAGS_IMG_HASH    (1 << 0)
+#define LOG_FLAGS_TRAILER_SUPPORT (1 << 1)
+
+#define LOGS_TLV_NUM_ENTRIES   (1 << 0)
+#define LOG_TLV_NUM_TLVS      (1 << 1)
 
 #if MYNEWT_VAL(LOG_VERSION) == 3
 struct log_entry_hdr {
@@ -140,16 +149,56 @@ struct log_entry_hdr {
     uint32_t ue_index;
     uint8_t ue_module;
     uint8_t ue_level;
-    uint8_t ue_etype:4;
-    uint8_t ue_flags:4;
+    uint8_t ue_etype : 4;
+    uint8_t ue_flags : 4;
     uint8_t ue_imghash[4];
-}__attribute__((__packed__));
+    /* Number of entries field which helps in calculating number of
+     * entries per log, these go on incrementing similar to an index
+     * but per log.
+     */
+    uint32_t ue_num_entries;
+} __attribute__((__packed__));
+
+struct log_tlv {
+    uint8_t len;
+    uint8_t tag;
+    /* Value is of variable size appended based on len,
+     * val is logged after the tag and len are logged
+     */
+} __attribute__((__packed__));
+
 #else
 #error "Unsupported log version"
 #endif
 
 #define LOG_BASE_ENTRY_HDR_SIZE (15)
 
+#define LOG_NUM_ENTRIES_SIZE (sizeof(((struct log *)0)->l_num_entries))
+#define LOG_NUM_TLVS_SIZE    (1)
+
+#if MYNEWT_VAL(LOG_FCB2)
+#define LF_MAX_ALIGN LOG_FCB2_MAX_ALIGN
+#else
+#define LF_MAX_ALIGN LOG_FCB_MAX_ALIGN
+#endif
+
+#define LOG_FCB_MAX_TLV_SIZE(__tlv_name__) \
+    /* sizeof(struct log_tlv)) + alignment */ \
+    (LF_MAX_ALIGN + \
+     /* Max size per value of TLV including alignment */ \
+     (LOG_ ## __tlv_name__ ## _SIZE/LF_MAX_ALIGN) ? \
+     (LOG_ ## __tlv_name__ ## _SIZE + LF_MAX_ALIGN) : \
+     LF_MAX_ALIGN)
+
+#define LOG_FCB_MAX_TLVS_SIZE LOG_FCB_MAX_TLV_SIZE(NUM_ENTRIES) + \
+    LOG_FCB_MAX_TLV_SIZE(NUM_TLVS)
+
+#define LOG_FCB_EXT_HDR_SIZE LOG_BASE_ENTRY_HDR_SIZE + LOG_IMG_HASHLEN + \
+    LF_MAX_ALIGN
+
+#define LOG_FCB_FLAT_BUF_SIZE (LOG_FCB_EXT_HDR_SIZE > LOG_FCB_MAX_TLVS_SIZE) ? 
\
+    LOG_FCB_EXT_HDR_SIZE : LOG_FCB_MAX_TLVS_SIZE
+
 #define LOG_MODULE_STR(module)      log_module_get_name(module)
 
 #if MYNEWT_VAL(LOG_LEVEL) <= LOG_LEVEL_DEBUG
@@ -214,6 +263,14 @@ struct log {
     uint16_t l_max_entry_len;   /* Log body length; if 0 disables check. */
 #if !MYNEWT_VAL(LOG_GLOBAL_IDX)
     uint32_t l_idx;
+#endif
+    uint32_t l_num_entries;
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+    log_trailer_len_cb *l_trailer_len_cb;
+    log_trailer_append_cb *l_trailer_append_cb;
+    log_trailer_mbuf_append_cb *l_trailer_mbuf_append_cb;
+    log_cbmem_trailer_append_cb *l_cbmem_trailer_append_cb;
+    log_cbmem_trailer_mbuf_append_cb *l_cbmem_trailer_mbuf_append_cb;
 #endif
 #if MYNEWT_VAL(LOG_STATS)
     STATS_SECT_DECL(logs) l_stats;
@@ -513,6 +570,18 @@ void log_printf(struct log *log, uint8_t module, uint8_t 
level,
 int log_read(struct log *log, const void *dptr, void *buf, uint16_t off,
         uint16_t len);
 
+/**
+ * Reads entry length from the specified log.
+ *
+ * @param log                   The log to read from.
+ * @param dptr                  Medium-specific data describing the area to
+ *                                  read from; typically obtained by a call to
+ *                                  `log_walk`.
+ * @return                      The number of bytes of entry length; 0 on 
failure.
+ */
+uint16_t
+log_read_entry_len(struct log *log, const void *dptr);
+
 /**
  * @brief Reads a single log entry header.
  *
@@ -530,11 +599,10 @@ int log_read_hdr(struct log *log, const void *dptr, 
struct log_entry_hdr *hdr);
  * @brief Reads the header length
  *
  * @param hdr Ptr to the header
- * 
+ *
  * @return Length of the header
  */
-uint16_t
-log_hdr_len(const struct log_entry_hdr *hdr);
+uint16_t log_hdr_len(const struct log_entry_hdr *hdr);
 
 /**
  * @brief Reads data from the body of a log entry into a flat buffer.
@@ -727,6 +795,33 @@ log_set_rotate_notify_cb(struct log *log, 
log_notify_rotate_cb *cb);
 int log_set_watermark(struct log *log, uint32_t index);
 #endif
 
+/**
+ * Fill number of entries
+ *
+ * @param log Ptr to log structure
+ * @param dptr Ptr to data to be read
+ * @param num_entries Ptr to number of entries
+ * @param offset Offset of the num of entries field in the log entry
+ *
+ * @return 0 on success, non-zero on failure
+ */
+int log_fill_num_entries(struct log *log, const void *dptr,
+                         uint32_t *num_entries,
+                         uint16_t offset);
+/**
+ * Fill number of tlvs
+ *
+ * @param log Ptr to log structure
+ * @param dptr Ptr to data to be read
+ * @param num_entries Ptr to number of entries
+ * @param offset Offset of the num of entries field in the log entry
+ *
+ * @return 0 on success, non-zero on failure
+ */
+int
+log_fill_num_tlvs(struct log *log, const void *dptr, uint8_t *num_tlvs,
+                  uint16_t offset);
+
 /**
  * Fill log current image hash
  *
@@ -737,6 +832,113 @@ int log_set_watermark(struct log *log, uint32_t index);
 int
 log_fill_current_img_hash(struct log_entry_hdr *hdr);
 
+/**
+ * Reads the log entry's header from the specified log and log index
+ *
+ * @param log                   The log to read from.
+ * @param idx                   Index of the log entry to read header from
+ * @param out_hdr               On success, the last entry header gets written
+ *                                  here.
+ *
+ * @return                      0 on success; nonzero on failure.
+ */
+int
+log_read_hdr_by_idx(struct log *log, uint32_t idx, struct log_entry_hdr 
*out_hdr);
+
+/**
+ * Get number of entries in log
+ *
+ * @param log The log to get number of entries for
+ * @param idx The log index to read number of entries from
+ * @param num_entries Ptr to fill up number of entries in log
+ */
+int
+log_get_entries(struct log *log, uint32_t idx, uint32_t *entries);
+
+/**
+ * Get the length of data in medium - storage (fcb/fcb2), memory or stream
+ *
+ * @param log The log to get number of entries for
+ * @param len Length in medium with padding if any
+ */
+int
+log_len_in_medium(struct log *log, uint16_t len);
+
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+/**
+ * @brief Reads the trailer length
+ *
+ * @param hdr Ptr to the header
+ *
+ * @return Length of the trailer
+ */
+uint16_t log_trailer_len(struct log *log, const struct log_entry_hdr *hdr);
+
+/**
+ * @brief Reads a single log entry trailer.
+ *
+ * @param log                   The log to read from.
+ * @param dptr                  Medium-specific data describing the area to
+ *                                  read from; typically obtained by a call to
+ *                                  `log_walk`.
+ * @param tlv                   tlv type
+ * @param buf                   Value buffer
+ *
+ * @return                      0 on success; nonzero on failure.
+ */
+int log_read_trailer(struct log *log, const void *dptr, uint16_t tlv, void 
*buf);
+
+/**
+ * Append trailer to the log entry
+ *
+ * @param log Pointer to the log
+ * @param buf Pointer to the buffer containing trailer
+ * @param buflen Length of the trailer
+ * @param loc Pointer to the log entry
+ */
+int
+log_trailer_append(struct log *log, uint8_t *buf, uint16_t buflen,
+                   void *loc);
+
+/**
+ * Append trailer in an mbuf to the log entry
+ *
+ * @param log Pointer to the log
+ * @param om Pointer to the os_mbuf
+ * @param loc Pointer to the log entry
+ */
+int
+log_mbuf_trailer_append(struct log *log, struct os_mbuf *om, void *loc);
+
+/**
+ * Register trailer callbacks
+ *
+ * @param log Pointer to the log
+ * @param ltac Pointer to the log trailer append callback
+ * @param ltlc Pointer to the log trailer length callback
+ */
+static inline void
+log_register_trailer_cbs(struct log *log, log_trailer_append_cb *ltac,
+                         log_trailer_len_cb *ltlc)
+{
+    log->l_trailer_append_cb = ltac;
+    log->l_trailer_len_cb = ltlc;
+}
+
+/**
+ * Register trailer callbacks
+ *
+ * @param log Pointer to the log
+ * @param ltmac Pointer to the log trailer mbuf append callback
+ */
+static inline void
+log_register_mbuf_trailer_cbs(struct log *log,
+                              log_trailer_mbuf_append_cb *ltmac)
+{
+    log->l_trailer_mbuf_append_cb = ltmac;
+}
+#endif
+
 /* Handler exports */
 #if MYNEWT_VAL(LOG_CONSOLE)
 extern const struct log_handler log_console_handler;
diff --git a/sys/log/full/selftest/align1_img_hash_num_entries/pkg.yml 
b/sys/log/full/selftest/align1_img_hash_num_entries/pkg.yml
new file mode 100644
index 000000000..56700bf5f
--- /dev/null
+++ b/sys/log/full/selftest/align1_img_hash_num_entries/pkg.yml
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+pkg.name: sys/log/full/selftest/align1_img_hash_num_entries
+pkg.type: unittest
+pkg.description: "Log unit tests; flash-alignment=1."
+pkg.author: "Apache Mynewt <[email protected]>"
+pkg.homepage: "http://mynewt.apache.org/";
+pkg.keywords:
+
+pkg.deps:
+    - "@apache-mynewt-core/sys/console/stub"
+    - "@apache-mynewt-core/sys/log/full"
+    - "@apache-mynewt-core/sys/log/full/selftest/util"
+    - "@apache-mynewt-core/test/testutil"
+    - "@apache-mynewt-core/boot/stub"
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c 
b/sys/log/full/selftest/align1_img_hash_num_entries/src/log_test_align1.c
similarity index 71%
copy from sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
copy to sys/log/full/selftest/align1_img_hash_num_entries/src/log_test_align1.c
index f48b87657..6fb73ffba 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
+++ b/sys/log/full/selftest/align1_img_hash_num_entries/src/log_test_align1.c
@@ -17,25 +17,17 @@
  * under the License.
  */
 
+#include "os/mynewt.h"
 #include "log_test_util/log_test_util.h"
 
-TEST_CASE_SELF(log_test_case_cbmem_printf)
+int
+main(int argc, char **argv)
 {
-    struct cbmem cbmem;
-    struct log log;
-    char *str;
-    int i;
+    log_test_suite_cbmem_flat();
+    log_test_suite_cbmem_mbuf();
+    log_test_suite_fcb_flat();
+    log_test_suite_fcb_mbuf();
+    log_test_suite_misc();
 
-    ltu_setup_cbmem(&cbmem, &log);
-
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
-
-        log_printf(&log, 0, 0, str, strlen(str));
-    }
-
-    ltu_verify_contents(&log);
+    return tu_any_failed;
 }
diff --git a/sys/log/full/selftest/align1_img_hash_num_entries/syscfg.yml 
b/sys/log/full/selftest/align1_img_hash_num_entries/syscfg.yml
new file mode 100644
index 000000000..049ef49ea
--- /dev/null
+++ b/sys/log/full/selftest/align1_img_hash_num_entries/syscfg.yml
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.vals:
+    LOG_FCB: 1
+    MCU_FLASH_MIN_WRITE_SIZE: 1
+
+    # The mbuf append tests allocate lots of mbufs; ensure no exhaustion.
+    MSYS_1_BLOCK_COUNT: 1000
+    LOG_FLAGS_IMAGE_HASH: 1
+    LOG_FLAGS_TRAILER_SUPPORT: 1
+    LOGS_TLV_NUM_ENTRIES: 1
+    IMGMGR_DUMMY_HDR: 1
+    LOG_MGMT: 0
+    IMG_MGMT: 0
diff --git a/sys/log/full/selftest/align1_num_entries/pkg.yml 
b/sys/log/full/selftest/align1_num_entries/pkg.yml
new file mode 100644
index 000000000..8a0bf7d10
--- /dev/null
+++ b/sys/log/full/selftest/align1_num_entries/pkg.yml
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+pkg.name: sys/log/full/selftest/align1_num_entries
+pkg.type: unittest
+pkg.description: "Log unit tests; flash-alignment=1."
+pkg.author: "Apache Mynewt <[email protected]>"
+pkg.homepage: "http://mynewt.apache.org/";
+pkg.keywords:
+
+pkg.deps:
+    - "@apache-mynewt-core/sys/console/stub"
+    - "@apache-mynewt-core/sys/log/full"
+    - "@apache-mynewt-core/sys/log/full/selftest/util"
+    - "@apache-mynewt-core/test/testutil"
+    - "@apache-mynewt-core/boot/stub"
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c 
b/sys/log/full/selftest/align1_num_entries/src/log_test_align1.c
similarity index 71%
copy from sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
copy to sys/log/full/selftest/align1_num_entries/src/log_test_align1.c
index f48b87657..6fb73ffba 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
+++ b/sys/log/full/selftest/align1_num_entries/src/log_test_align1.c
@@ -17,25 +17,17 @@
  * under the License.
  */
 
+#include "os/mynewt.h"
 #include "log_test_util/log_test_util.h"
 
-TEST_CASE_SELF(log_test_case_cbmem_printf)
+int
+main(int argc, char **argv)
 {
-    struct cbmem cbmem;
-    struct log log;
-    char *str;
-    int i;
+    log_test_suite_cbmem_flat();
+    log_test_suite_cbmem_mbuf();
+    log_test_suite_fcb_flat();
+    log_test_suite_fcb_mbuf();
+    log_test_suite_misc();
 
-    ltu_setup_cbmem(&cbmem, &log);
-
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
-
-        log_printf(&log, 0, 0, str, strlen(str));
-    }
-
-    ltu_verify_contents(&log);
+    return tu_any_failed;
 }
diff --git a/sys/log/full/selftest/align1_num_entries/syscfg.yml 
b/sys/log/full/selftest/align1_num_entries/syscfg.yml
new file mode 100644
index 000000000..7171a4122
--- /dev/null
+++ b/sys/log/full/selftest/align1_num_entries/syscfg.yml
@@ -0,0 +1,29 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.vals:
+    LOG_FCB: 1
+    MCU_FLASH_MIN_WRITE_SIZE: 1
+
+    # The mbuf append tests allocate lots of mbufs; ensure no exhaustion.
+    MSYS_1_BLOCK_COUNT: 1000
+    LOG_FLAGS_TRAILER_SUPPORT: 1
+    LOGS_TLV_NUM_ENTRIES: 1
+    IMGMGR_DUMMY_HDR: 1
+    LOG_MGMT: 0
+    IMG_MGMT: 0
diff --git a/sys/log/full/selftest/align4_num_entries/pkg.yml 
b/sys/log/full/selftest/align4_num_entries/pkg.yml
new file mode 100644
index 000000000..339101d7b
--- /dev/null
+++ b/sys/log/full/selftest/align4_num_entries/pkg.yml
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+pkg.name: sys/log/full/selftest/align4_num_entries
+pkg.type: unittest
+pkg.description: "Log unit tests; flash-alignment=4."
+pkg.author: "Apache Mynewt <[email protected]>"
+pkg.homepage: "http://mynewt.apache.org/";
+pkg.keywords:
+
+pkg.deps:
+    - "@apache-mynewt-core/sys/console/stub"
+    - "@apache-mynewt-core/sys/log/full"
+    - "@apache-mynewt-core/sys/log/full/selftest/util"
+    - "@apache-mynewt-core/test/testutil"
+    - "@apache-mynewt-core/boot/stub"
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c 
b/sys/log/full/selftest/align4_num_entries/src/log_test_align1.c
similarity index 71%
copy from sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
copy to sys/log/full/selftest/align4_num_entries/src/log_test_align1.c
index f48b87657..6fb73ffba 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
+++ b/sys/log/full/selftest/align4_num_entries/src/log_test_align1.c
@@ -17,25 +17,17 @@
  * under the License.
  */
 
+#include "os/mynewt.h"
 #include "log_test_util/log_test_util.h"
 
-TEST_CASE_SELF(log_test_case_cbmem_printf)
+int
+main(int argc, char **argv)
 {
-    struct cbmem cbmem;
-    struct log log;
-    char *str;
-    int i;
+    log_test_suite_cbmem_flat();
+    log_test_suite_cbmem_mbuf();
+    log_test_suite_fcb_flat();
+    log_test_suite_fcb_mbuf();
+    log_test_suite_misc();
 
-    ltu_setup_cbmem(&cbmem, &log);
-
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
-
-        log_printf(&log, 0, 0, str, strlen(str));
-    }
-
-    ltu_verify_contents(&log);
+    return tu_any_failed;
 }
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c 
b/sys/log/full/selftest/align4_num_entries/src/log_test_align4.c
similarity index 71%
copy from sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
copy to sys/log/full/selftest/align4_num_entries/src/log_test_align4.c
index f48b87657..b1c5a1d23 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
+++ b/sys/log/full/selftest/align4_num_entries/src/log_test_align4.c
@@ -17,25 +17,25 @@
  * under the License.
  */
 
+#include "os/mynewt.h"
 #include "log_test_util/log_test_util.h"
 
-TEST_CASE_SELF(log_test_case_cbmem_printf)
+int
+main(int argc, char **argv)
 {
-    struct cbmem cbmem;
-    struct log log;
-    char *str;
-    int i;
+    log_test_suite_cbmem_flat();
 
-    ltu_setup_cbmem(&cbmem, &log);
+    log_test_suite_cbmem_mbuf();
+    log_test_suite_fcb_flat();
 
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
+#if 0
+    /* Current fcb mbuf implementation supports only
+     * 1 byte alignment
+     */
+    log_test_suite_fcb_mbuf();
+#endif
 
-        log_printf(&log, 0, 0, str, strlen(str));
-    }
+    log_test_suite_misc();
 
-    ltu_verify_contents(&log);
+    return tu_any_failed;
 }
diff --git a/sys/log/full/selftest/align4_num_entries/syscfg.yml 
b/sys/log/full/selftest/align4_num_entries/syscfg.yml
new file mode 100644
index 000000000..7171a4122
--- /dev/null
+++ b/sys/log/full/selftest/align4_num_entries/syscfg.yml
@@ -0,0 +1,29 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.vals:
+    LOG_FCB: 1
+    MCU_FLASH_MIN_WRITE_SIZE: 1
+
+    # The mbuf append tests allocate lots of mbufs; ensure no exhaustion.
+    MSYS_1_BLOCK_COUNT: 1000
+    LOG_FLAGS_TRAILER_SUPPORT: 1
+    LOGS_TLV_NUM_ENTRIES: 1
+    IMGMGR_DUMMY_HDR: 1
+    LOG_MGMT: 0
+    IMG_MGMT: 0
diff --git a/sys/log/full/selftest/fcb2_align1_imghash/pkg.yml 
b/sys/log/full/selftest/fcb2_align1_imghash/pkg.yml
new file mode 100644
index 000000000..f931c7d49
--- /dev/null
+++ b/sys/log/full/selftest/fcb2_align1_imghash/pkg.yml
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+pkg.name: sys/log/full/selftest/fcb2_align1_imghash
+pkg.type: unittest
+pkg.description: "Log unit tests; FCB2 flash-alignment=1."
+pkg.author: "Apache Mynewt <[email protected]>"
+pkg.homepage: "http://mynewt.apache.org/";
+pkg.keywords:
+
+pkg.deps:
+    - "@apache-mynewt-core/sys/console/stub"
+    - "@apache-mynewt-core/sys/log/full"
+    - "@apache-mynewt-core/sys/log/full/selftest/util"
+    - "@apache-mynewt-core/test/testutil"
+    - "@apache-mynewt-core/boot/stub"
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c 
b/sys/log/full/selftest/fcb2_align1_imghash/src/log_test_align1.c
similarity index 71%
copy from sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
copy to sys/log/full/selftest/fcb2_align1_imghash/src/log_test_align1.c
index f48b87657..6fb73ffba 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
+++ b/sys/log/full/selftest/fcb2_align1_imghash/src/log_test_align1.c
@@ -17,25 +17,17 @@
  * under the License.
  */
 
+#include "os/mynewt.h"
 #include "log_test_util/log_test_util.h"
 
-TEST_CASE_SELF(log_test_case_cbmem_printf)
+int
+main(int argc, char **argv)
 {
-    struct cbmem cbmem;
-    struct log log;
-    char *str;
-    int i;
+    log_test_suite_cbmem_flat();
+    log_test_suite_cbmem_mbuf();
+    log_test_suite_fcb_flat();
+    log_test_suite_fcb_mbuf();
+    log_test_suite_misc();
 
-    ltu_setup_cbmem(&cbmem, &log);
-
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
-
-        log_printf(&log, 0, 0, str, strlen(str));
-    }
-
-    ltu_verify_contents(&log);
+    return tu_any_failed;
 }
diff --git a/sys/log/full/selftest/fcb2_align1_imghash/syscfg.yml 
b/sys/log/full/selftest/fcb2_align1_imghash/syscfg.yml
new file mode 100644
index 000000000..fa4b33f97
--- /dev/null
+++ b/sys/log/full/selftest/fcb2_align1_imghash/syscfg.yml
@@ -0,0 +1,29 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.vals:
+    LOG_FCB2: 1
+    MCU_FLASH_MIN_WRITE_SIZE: 1
+    LOG_FLAGS_IMAGE_HASH: 1
+
+    # The mbuf append tests allocate lots of mbufs; ensure no exhaustion.
+    MSYS_1_BLOCK_COUNT: 1000
+    LOG_FLAGS_IMAGE_HASH: 1
+    IMGMGR_DUMMY_HDR: 1
+    LOG_MGMT: 0
+    IMG_MGMT: 0
diff --git a/sys/log/full/selftest/fcb2_align1_imghash_num_entries/pkg.yml 
b/sys/log/full/selftest/fcb2_align1_imghash_num_entries/pkg.yml
new file mode 100644
index 000000000..e84f61ad7
--- /dev/null
+++ b/sys/log/full/selftest/fcb2_align1_imghash_num_entries/pkg.yml
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+pkg.name: sys/log/full/selftest/fcb2_align1_imghash_num_entries
+pkg.type: unittest
+pkg.description: "Log unit tests; FCB2 flash-alignment=1."
+pkg.author: "Apache Mynewt <[email protected]>"
+pkg.homepage: "http://mynewt.apache.org/";
+pkg.keywords:
+
+pkg.deps:
+    - "@apache-mynewt-core/sys/console/stub"
+    - "@apache-mynewt-core/sys/log/full"
+    - "@apache-mynewt-core/sys/log/full/selftest/util"
+    - "@apache-mynewt-core/test/testutil"
+    - "@apache-mynewt-core/boot/stub"
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c 
b/sys/log/full/selftest/fcb2_align1_imghash_num_entries/src/log_test_align1.c
similarity index 71%
copy from sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
copy to 
sys/log/full/selftest/fcb2_align1_imghash_num_entries/src/log_test_align1.c
index f48b87657..6fb73ffba 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
+++ 
b/sys/log/full/selftest/fcb2_align1_imghash_num_entries/src/log_test_align1.c
@@ -17,25 +17,17 @@
  * under the License.
  */
 
+#include "os/mynewt.h"
 #include "log_test_util/log_test_util.h"
 
-TEST_CASE_SELF(log_test_case_cbmem_printf)
+int
+main(int argc, char **argv)
 {
-    struct cbmem cbmem;
-    struct log log;
-    char *str;
-    int i;
+    log_test_suite_cbmem_flat();
+    log_test_suite_cbmem_mbuf();
+    log_test_suite_fcb_flat();
+    log_test_suite_fcb_mbuf();
+    log_test_suite_misc();
 
-    ltu_setup_cbmem(&cbmem, &log);
-
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
-
-        log_printf(&log, 0, 0, str, strlen(str));
-    }
-
-    ltu_verify_contents(&log);
+    return tu_any_failed;
 }
diff --git a/sys/log/full/selftest/fcb2_align1_imghash_num_entries/syscfg.yml 
b/sys/log/full/selftest/fcb2_align1_imghash_num_entries/syscfg.yml
new file mode 100644
index 000000000..190b3e9d0
--- /dev/null
+++ b/sys/log/full/selftest/fcb2_align1_imghash_num_entries/syscfg.yml
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.vals:
+    LOG_FCB2: 1
+    MCU_FLASH_MIN_WRITE_SIZE: 1
+
+    # The mbuf append tests allocate lots of mbufs; ensure no exhaustion.
+    MSYS_1_BLOCK_COUNT: 1000
+    LOG_FLAGS_IMAGE_HASH: 1
+    LOG_FLAGS_TRAILER_SUPPORT: 1
+    LOGS_TLV_NUM_ENTRIES: 1
+    IMGMGR_DUMMY_HDR: 1
+    LOG_MGMT: 0
+    IMG_MGMT: 0
diff --git a/sys/log/full/selftest/util/include/log_test_util/log_test_util.h 
b/sys/log/full/selftest/util/include/log_test_util/log_test_util.h
index 90b77e498..4c23c3733 100644
--- a/sys/log/full/selftest/util/include/log_test_util/log_test_util.h
+++ b/sys/log/full/selftest/util/include/log_test_util/log_test_util.h
@@ -41,6 +41,7 @@ extern struct fcb2 log_fcb;
 #endif
 extern struct log my_log;
 extern char *ltu_str_logs[];
+extern uint8_t dummy_log_arr[];
 
 struct os_mbuf *ltu_flat_to_fragged_mbuf(const void *flat, int len,
                                          int frag_sz);
@@ -49,6 +50,9 @@ void ltu_setup_2fcbs(struct fcb_log *fcb_log1, struct log 
*log1,
                      struct fcb_log *fcb_log2, struct log *log2);
 void ltu_setup_cbmem(struct cbmem *cbmem, struct log *log);
 void ltu_verify_contents(struct log *log);
+uint16_t *ltu_get_ltu_off_arr(void);
+uint16_t ltu_init_arr(void);
+int ltu_num_strs(void);
 
 TEST_SUITE_DECL(log_test_suite_cbmem_flat);
 TEST_CASE_DECL(log_test_case_cbmem_append);
diff --git a/sys/log/full/selftest/util/src/log_test_util.c 
b/sys/log/full/selftest/util/src/log_test_util.c
index 2447cbb61..b8a04cc7d 100644
--- a/sys/log/full/selftest/util/src/log_test_util.c
+++ b/sys/log/full/selftest/util/src/log_test_util.c
@@ -18,6 +18,7 @@
  */
 
 #include "log_test_util/log_test_util.h"
+#include "log/log.h"
 
 #if MYNEWT_VAL(LOG_FCB)
 static struct flash_area fcb_areas[] = {
@@ -46,6 +47,37 @@ static struct flash_sector_range fcb_range = {
 static int ltu_str_idx = 0;
 static int ltu_str_max_idx = 0;
 
+struct dummy_log {
+    struct log_entry_hdr hdr;
+    struct log_tlv tlv;
+    uint32_t num_entries;
+};
+
+struct dummy_log dummy_log = {
+    .hdr = {
+        .ue_ts = 1,
+        .ue_module = 2,
+        .ue_etype = 3,
+        .ue_flags = 0
+#if MYNEWT_VAL(LOG_FLAGS_IMAGE_HASH)
+                    | LOG_FLAGS_IMG_HASH
+#endif
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+                    | LOG_FLAGS_TRAILER_SUPPORT
+#endif
+        ,
+        .ue_etype = 0,
+        .ue_imghash = {1, 2, 3, 4},
+        .ue_level = 3,
+        .ue_num_entries = 5
+    },
+    .tlv = {
+        .tag = LOGS_TLV_NUM_ENTRIES,
+        .len = LOG_NUM_ENTRIES_SIZE
+    },
+    .num_entries = 0,
+};
+
 char *ltu_str_logs[] = {
     "testdata",
     "1testdata2",
@@ -54,6 +86,10 @@ char *ltu_str_logs[] = {
     NULL
 };
 
+uint16_t ltu_off_arr[5];
+
+uint8_t dummy_log_arr[2048];
+
 static uint8_t ltu_cbmem_buf[2048];
 
 int
@@ -66,6 +102,41 @@ ltu_num_strs(void)
     return i;
 }
 
+uint16_t *
+ltu_get_ltu_off_arr(void)
+{
+    return ltu_off_arr;
+}
+
+uint16_t
+ltu_init_arr(void)
+{
+    int i;
+    uint16_t offset = 0;
+
+    for (i = 0; i < ltu_num_strs(); i++) {
+        TEST_ASSERT_FATAL(offset <= 2048);
+        ltu_off_arr[i] = offset;
+        memcpy(dummy_log_arr + offset, &dummy_log.hdr, 
LOG_BASE_ENTRY_HDR_SIZE);
+        offset += LOG_BASE_ENTRY_HDR_SIZE;
+#if MYNEWT_VAL(LOG_FLAGS_IMAGE_HASH)
+        memcpy(dummy_log_arr + offset,
+               dummy_log.hdr.ue_imghash, LOG_IMG_HASHLEN);
+        offset += LOG_IMG_HASHLEN;
+#endif
+        memcpy(dummy_log_arr + offset, ltu_str_logs[i], 
strlen(ltu_str_logs[i]));
+        offset += strlen(ltu_str_logs[i]);
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT) && MYNEWT_VAL(LOGS_TLV_NUM_ENTRIES)
+        memcpy(dummy_log_arr + offset, &dummy_log.tlv, sizeof(struct log_tlv));
+        offset += sizeof(struct log_tlv);
+        memcpy(dummy_log_arr + offset, &dummy_log.num_entries, 
LOG_NUM_ENTRIES_SIZE);
+        offset += LOG_NUM_ENTRIES_SIZE;
+#endif
+    }
+    ltu_off_arr[i] = offset;
+    return offset;
+}
+
 struct os_mbuf *
 ltu_flat_to_fragged_mbuf(const void *flat, int len, int frag_sz)
 {
@@ -190,6 +261,8 @@ ltu_walk_verify(struct log *log, struct log_offset 
*log_offset,
     char data[128];
     int dlen;
     uint16_t hdr_len;
+    uint16_t trailer_len;
+    uint16_t offset = 0;
 
     TEST_ASSERT(ltu_str_idx < ltu_str_max_idx);
 
@@ -197,9 +270,17 @@ ltu_walk_verify(struct log *log, struct log_offset 
*log_offset,
 
     rc = log_read(log, dptr, &ueh, 0, LOG_BASE_ENTRY_HDR_SIZE);
     TEST_ASSERT(rc == LOG_BASE_ENTRY_HDR_SIZE);
+    offset = LOG_BASE_ENTRY_HDR_SIZE;
+
+    if (ueh.ue_flags & LOG_FLAGS_IMG_HASH) {
+        rc = log_read(log, dptr, data, offset, LOG_IMG_HASHLEN);
+        TEST_ASSERT(rc == LOG_IMG_HASHLEN);
+        offset += LOG_IMG_HASHLEN;
+    }
 
     hdr_len = log_hdr_len(&ueh);
-    dlen = len - hdr_len;
+    trailer_len = log_trailer_len(log, &ueh);
+    dlen = len - hdr_len - trailer_len;
     TEST_ASSERT(dlen < sizeof(data));
 
     rc = log_read(log, dptr, data, hdr_len, dlen);
@@ -218,6 +299,12 @@ ltu_walk_verify(struct log *log, struct log_offset 
*log_offset,
     rc = log_read_body(log, dptr, data, 0, dlen);
     TEST_ASSERT(rc == dlen);
 
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT) && MYNEWT_VAL(LOGS_TLV_NUM_ENTRIES)
+    uint32_t num_entries;;
+    rc = log_read_trailer(log, dptr, LOGS_TLV_NUM_ENTRIES, &num_entries);
+    TEST_ASSERT(rc == 0);
+#endif
+
     TEST_ASSERT(strlen(ltu_str_logs[ltu_str_idx]) == dlen);
     TEST_ASSERT(!memcmp(ltu_str_logs[ltu_str_idx], data, dlen));
 
@@ -263,6 +350,8 @@ ltu_walk_body_verify(struct log *log, struct log_offset 
*log_offset,
 
     TEST_ASSERT(len < sizeof(data));
 
+    len -= log_trailer_len(log, euh);
+
     rc = log_read_body(log, dptr, data, 0, len);
     TEST_ASSERT(rc == len);
 
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append.c 
b/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append.c
index 180f3774f..5f5d9d17a 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append.c
+++ b/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append.c
@@ -23,23 +23,26 @@ TEST_CASE_SELF(log_test_case_cbmem_append)
 {
     struct cbmem cbmem;
     struct log log;
-    uint8_t buf[256];
-    char *str;
-    int body_len;
+    uint16_t len = 0;
+    uint16_t *off_arr;
     int i;
     int rc;
+    int num_strs = ltu_num_strs();
+    struct log_entry_hdr *hdr;
 
     ltu_setup_cbmem(&cbmem, &log);
+    len = ltu_init_arr();
+    TEST_ASSERT_FATAL(len != 0);
 
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
+    off_arr = ltu_get_ltu_off_arr();
+    TEST_ASSERT_FATAL(off_arr != NULL);
 
-        body_len = strlen(str);
-        memcpy(buf + LOG_HDR_SIZE, str, body_len);
-        rc = log_append_typed(&log, 0, 0, LOG_ETYPE_STRING, buf, body_len);
+    for (i = 0; i < num_strs; i++) {
+       hdr = (struct log_entry_hdr *)(dummy_log_arr + off_arr[i]);
+        len = off_arr[i+1] - off_arr[i] - log_hdr_len(hdr) - 
log_trailer_len(&log, hdr);
+        rc = log_append_typed(&log, 2, 3, LOG_ETYPE_STRING,
+                              dummy_log_arr + off_arr[i],
+                              len);
         TEST_ASSERT_FATAL(rc == 0);
     }
 
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_body.c 
b/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_body.c
index 0a7fb1c11..8948bacad 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_body.c
+++ b/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_body.c
@@ -23,18 +23,28 @@ TEST_CASE_SELF(log_test_case_cbmem_append_body)
 {
     struct cbmem cbmem;
     struct log log;
-    char *str;
+    uint16_t len = 0;
+    uint16_t *off_arr;
     int i;
+    int rc;
+    struct log_entry_hdr *hdr;
+    int num_strs = ltu_num_strs();
 
     ltu_setup_cbmem(&cbmem, &log);
+    len = ltu_init_arr();
+    TEST_ASSERT_FATAL(len != 0);
 
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
+    off_arr = ltu_get_ltu_off_arr();
+    TEST_ASSERT_FATAL(off_arr != NULL);
 
-        log_append_body(&log, 0, 0, LOG_ETYPE_STRING, str, strlen(str));
+    for (i = 0; i < num_strs; i++) {
+        hdr = (struct log_entry_hdr *)(dummy_log_arr + off_arr[i]);
+        len = off_arr[i + 1] - off_arr[i] -
+              log_hdr_len(hdr) - log_trailer_len(&log, hdr);
+        rc = log_append_body(&log, 2, 3, LOG_ETYPE_STRING,
+                             dummy_log_arr + off_arr[i] + log_hdr_len(hdr),
+                             len);
+        TEST_ASSERT_FATAL(rc == 0);
     }
 
     ltu_verify_contents(&log);
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_mbuf.c 
b/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_mbuf.c
index 3bf305549..aaeab19d3 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_mbuf.c
+++ b/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_mbuf.c
@@ -22,28 +22,28 @@
 TEST_CASE_SELF(log_test_case_cbmem_append_mbuf)
 {
     struct cbmem cbmem;
-    struct os_mbuf *om;
     struct log log;
-    char *str;
-    int rc;
+    struct os_mbuf *om;
+    uint16_t len = 0;
+    uint16_t *off_arr;
     int i;
+    int rc;
+    int num_strs = ltu_num_strs();
 
     ltu_setup_cbmem(&cbmem, &log);
+    len = ltu_init_arr();
+    TEST_ASSERT_FATAL(len != 0);
 
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
+    off_arr = ltu_get_ltu_off_arr();
+    TEST_ASSERT_FATAL(off_arr != NULL);
 
+    for (i = 0; i < num_strs; i++) {
+        len = off_arr[i+1] - off_arr[i];
         /* Split chain into several mbufs. */
-        om = ltu_flat_to_fragged_mbuf(str, strlen(str), 2);
-
-        /* Prepend space for the entry header. */
-        om = os_mbuf_prepend(om, LOG_HDR_SIZE);
-        TEST_ASSERT(om != NULL);
+        om = ltu_flat_to_fragged_mbuf(dummy_log_arr + off_arr[i],
+                                      len, 2);
 
-        rc = log_append_mbuf_typed(&log, 0, 0, LOG_ETYPE_STRING, om);
+        rc = log_append_mbuf_typed(&log, 2, 3, LOG_ETYPE_STRING, om);
         TEST_ASSERT_FATAL(rc == 0);
     }
 
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_mbuf_body.c
 
b/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_mbuf_body.c
index fddb1bbc8..86775f807 100644
--- 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_mbuf_body.c
+++ 
b/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_append_mbuf_body.c
@@ -24,22 +24,30 @@ TEST_CASE_SELF(log_test_case_cbmem_append_mbuf_body)
     struct cbmem cbmem;
     struct os_mbuf *om;
     struct log log;
-    char *str;
     int rc;
     int i;
+    uint16_t len;
+    int num_strs = ltu_num_strs();
+    struct log_entry_hdr *hdr;
+    uint16_t *off_arr;
 
     ltu_setup_cbmem(&cbmem, &log);
+    len = ltu_init_arr();
+    TEST_ASSERT_FATAL(len != 0);
 
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
+    off_arr = ltu_get_ltu_off_arr();
+    TEST_ASSERT_FATAL(off_arr != NULL);
+
+    for (i = 0; i < num_strs; i++) {
+        hdr = (struct log_entry_hdr *)(dummy_log_arr + off_arr[i]);
+        len = off_arr[i + 1] - off_arr[i] -
+              log_hdr_len(hdr);
 
         /* Split chain into several mbufs. */
-        om = ltu_flat_to_fragged_mbuf(str, strlen(str), 2);
+        om = ltu_flat_to_fragged_mbuf(dummy_log_arr + off_arr[i] + 
log_hdr_len(hdr),
+                                      len, 2);
 
-        rc = log_append_mbuf_body(&log, 0, 0, LOG_ETYPE_STRING, om);
+        rc = log_append_mbuf_body(&log, 2, 3, LOG_ETYPE_STRING, om);
         TEST_ASSERT_FATAL(rc == 0);
     }
 
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c 
b/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
index f48b87657..f29bdcdb1 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
+++ b/sys/log/full/selftest/util/src/testcases/log_test_case_cbmem_printf.c
@@ -23,18 +23,27 @@ TEST_CASE_SELF(log_test_case_cbmem_printf)
 {
     struct cbmem cbmem;
     struct log log;
-    char *str;
     int i;
+    uint16_t len = 0;
+    int num_strs = ltu_num_strs();
+    uint16_t *off_arr;
+    struct log_entry_hdr *hdr;
+    char data[256];
 
     ltu_setup_cbmem(&cbmem, &log);
+    len = ltu_init_arr();
+    TEST_ASSERT_FATAL(len != 0);
 
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
+    off_arr = ltu_get_ltu_off_arr();
+    TEST_ASSERT_FATAL(off_arr != NULL);
 
-        log_printf(&log, 0, 0, str, strlen(str));
+    for (i = 0; i < num_strs; i++) {
+       hdr = (struct log_entry_hdr *)(dummy_log_arr + off_arr[i]);
+        len = off_arr[i+1] - off_arr[i] - log_hdr_len(hdr) - 
log_trailer_len(&log, hdr);
+       memcpy(data, dummy_log_arr + off_arr[i] + log_hdr_len(hdr),
+              len);
+       data[len] = '\0';
+        log_printf(&log, 0, 0, data, len);
     }
 
     ltu_verify_contents(&log);
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append.c 
b/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append.c
index 808188ce1..3fe8f4e28 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append.c
+++ b/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append.c
@@ -23,22 +23,27 @@ TEST_CASE_SELF(log_test_case_fcb_append)
 {
     struct fcb_log fcb_log;
     struct log log;
-    uint8_t buf[256];
-    char *str;
-    int body_len;
+    uint16_t len = 0;
+    uint16_t *off_arr;
     int i;
+    int rc;
+    int num_strs = ltu_num_strs();
+    struct log_entry_hdr *hdr;
 
     ltu_setup_fcb(&fcb_log, &log);
+    len = ltu_init_arr();
+    TEST_ASSERT_FATAL(len != 0);
 
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
+    off_arr = ltu_get_ltu_off_arr();
+    TEST_ASSERT_FATAL(off_arr != NULL);
 
-        body_len = strlen(str);
-        memcpy(buf + LOG_HDR_SIZE, str, body_len);
-        log_append_typed(&log, 0, 0, LOG_ETYPE_STRING, buf, body_len);
+    for (i = 0;i < num_strs; i++) {
+       hdr = (struct log_entry_hdr *)(dummy_log_arr + off_arr[i]);
+        len = off_arr[i+1] - off_arr[i] - log_hdr_len(hdr) - 
log_trailer_len(&log, hdr);
+        rc = log_append_typed(&log, 2, 3, LOG_ETYPE_STRING,
+                              dummy_log_arr + off_arr[i],
+                              len);
+        TEST_ASSERT_FATAL(rc == 0);
     }
 
     ltu_verify_contents(&log);
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_body.c 
b/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_body.c
index 1d0a86637..996ff647e 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_body.c
+++ b/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_body.c
@@ -23,18 +23,30 @@ TEST_CASE_SELF(log_test_case_fcb_append_body)
 {
     struct fcb_log fcb_log;
     struct log log;
-    char *str;
+    uint16_t len = 0;
+    uint16_t *off_arr;
     int i;
+    int rc;
+    struct log_entry_hdr *hdr;
+    int num_strs = ltu_num_strs();
 
     ltu_setup_fcb(&fcb_log, &log);
+    len = ltu_init_arr();
+    TEST_ASSERT_FATAL(len != 0);
 
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
-        log_append_body(&log, 0, 0, LOG_ETYPE_STRING, str, strlen(str));
+    off_arr = ltu_get_ltu_off_arr();
+    TEST_ASSERT_FATAL(off_arr != NULL);
+
+    for (i = 0; i < num_strs; i++) {
+        hdr = (struct log_entry_hdr *)(dummy_log_arr + off_arr[i]);
+        len = off_arr[i + 1] - off_arr[i] -
+              log_hdr_len(hdr) - log_trailer_len(&log, hdr);
+        rc = log_append_body(&log, 2, 3, LOG_ETYPE_STRING,
+                             dummy_log_arr + off_arr[i] + log_hdr_len(hdr),
+                             len);
+        TEST_ASSERT_FATAL(rc == 0);
     }
 
     ltu_verify_contents(&log);
+
 }
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_mbuf.c 
b/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_mbuf.c
index e566c78bd..7a3c2b035 100644
--- a/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_mbuf.c
+++ b/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_mbuf.c
@@ -24,26 +24,28 @@ TEST_CASE_SELF(log_test_case_fcb_append_mbuf)
     struct fcb_log fcb_log;
     struct os_mbuf *om;
     struct log log;
-    char *str;
     int rc;
     int i;
+    int num_strs = ltu_num_strs();
+    struct log_entry_hdr *hdr;
+    uint16_t *off_arr;
+    uint16_t len;
 
     ltu_setup_fcb(&fcb_log, &log);
+    len = ltu_init_arr();
+    TEST_ASSERT_FATAL(len != 0);
 
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
+    off_arr = ltu_get_ltu_off_arr();
+    TEST_ASSERT_FATAL(off_arr != NULL);
 
+    for (i = 0; i < num_strs; i++) {
+        hdr = (struct log_entry_hdr *)(dummy_log_arr + off_arr[i]);
+        len = off_arr[i+1] - off_arr[i] - log_trailer_len(&log, hdr);
         /* Split chain into several mbufs. */
-        om = ltu_flat_to_fragged_mbuf(str, strlen(str), 2);
+        om = ltu_flat_to_fragged_mbuf(dummy_log_arr + off_arr[i],
+                                      len, 2);
 
-        /* Prepend space for the entry header. */
-        om = os_mbuf_prepend(om, LOG_HDR_SIZE);
-        TEST_ASSERT(om != NULL);
-
-        rc = log_append_mbuf_typed(&log, 0, 0, LOG_ETYPE_STRING, om);
+        rc = log_append_mbuf_typed(&log, 2, 3, LOG_ETYPE_STRING, om);
         TEST_ASSERT_FATAL(rc == 0);
     }
 
diff --git 
a/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_mbuf_body.c 
b/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_mbuf_body.c
index 25d536479..bc980835a 100644
--- 
a/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_mbuf_body.c
+++ 
b/sys/log/full/selftest/util/src/testcases/log_test_case_fcb_append_mbuf_body.c
@@ -24,22 +24,30 @@ TEST_CASE_SELF(log_test_case_fcb_append_mbuf_body)
     struct fcb_log fcb_log;
     struct os_mbuf *om;
     struct log log;
-    char *str;
     int rc;
     int i;
+    int num_strs = ltu_num_strs();
+    struct log_entry_hdr *hdr;
+    uint16_t *off_arr;
+    uint16_t len;
 
     ltu_setup_fcb(&fcb_log, &log);
+    len = ltu_init_arr();
+    TEST_ASSERT_FATAL(len != 0);
 
-    for (i = 0; ; i++) {
-        str = ltu_str_logs[i];
-        if (!str) {
-            break;
-        }
+    off_arr = ltu_get_ltu_off_arr();
+    TEST_ASSERT_FATAL(off_arr != NULL);
+
+    for (i = 0; i < num_strs; i++) {
+        hdr = (struct log_entry_hdr *)(dummy_log_arr + off_arr[i]);
+        len = off_arr[i + 1] - off_arr[i] -
+              log_hdr_len(hdr) - log_trailer_len(&log, hdr);
 
         /* Split chain into several mbufs. */
-        om = ltu_flat_to_fragged_mbuf(str, strlen(str), 2);
+        om = ltu_flat_to_fragged_mbuf(dummy_log_arr + off_arr[i] + 
log_hdr_len(hdr),
+                                      len, 2);
 
-        rc = log_append_mbuf_body(&log, 0, 0, LOG_ETYPE_STRING, om);
+        rc = log_append_mbuf_body(&log, 2, 3, LOG_ETYPE_STRING, om);
         TEST_ASSERT_FATAL(rc == 0);
     }
 
diff --git a/sys/log/full/src/log.c b/sys/log/full/src/log.c
index 978924e6b..3fde2987f 100644
--- a/sys/log/full/src/log.c
+++ b/sys/log/full/src/log.c
@@ -299,11 +299,77 @@ struct log_read_hdr_arg {
 };
 
 static int
-log_read_hdr_walk(struct log *log, struct log_offset *log_offset, const void 
*dptr,
-                  uint16_t len)
+log_update_num_entries_hdr_walk(struct log *log, struct log_offset *log_offset,
+                                const void *dptr, uint16_t len)
+{
+    uint32_t *num_entries;
+    uint16_t offset = 0;
+    int rc;
+    struct log_entry_hdr hdr;
+
+    (void)offset;
+    num_entries = log_offset->lo_arg;
+
+    rc = log_read(log, dptr, &hdr, 0, LOG_BASE_ENTRY_HDR_SIZE);
+    if (rc >= LOG_BASE_ENTRY_HDR_SIZE) {
+        *num_entries += 1;
+        return 0;
+    }
+
+    return -1;
+}
+
+static int
+log_process_tlvs(struct log *log, const void *dptr, uint8_t num_tlvs,
+                 uint16_t tlv_tag, void *buf, uint16_t len)
+{
+    struct log_tlv *tlv;
+    uint8_t tmpbuf[sizeof(*tlv) + 8] = {0};
+    uint16_t offset;
+    int tlv_len;
+    int rc = SYS_ENOENT;
+
+    offset = len;
+    tlv_len = log_len_in_medium(log, sizeof(*tlv));
+    do {
+        offset -= tlv_len;
+        rc = log_read(log, dptr, tmpbuf, offset, tlv_len);
+        if (rc < tlv_len) {
+            return SYS_EINVAL;
+        }
+        tlv = (struct log_tlv *)tmpbuf;
+
+        offset -= log_len_in_medium(log, tlv->len);
+        if (tlv->tag != tlv_tag) {
+            continue;
+        }
+
+        switch (tlv_tag) {
+        case LOGS_TLV_NUM_ENTRIES:
+            rc = log_fill_num_entries(log, dptr, buf, offset);
+            break;
+        case LOG_TLV_NUM_TLVS:
+            rc = log_fill_num_tlvs(log, dptr, buf, offset);
+            break;
+        default:
+            return SYS_ENOTSUP;
+        }
+
+        if (!rc) {
+            return SYS_EOK;
+        }
+    } while (offset && num_tlvs);
+
+    return SYS_EOK;
+}
+
+static int
+log_read_hdr_walk(struct log *log, struct log_offset *log_offset,
+                  const void *dptr, uint16_t len)
 {
     struct log_read_hdr_arg *arg;
     int rc;
+    uint8_t num_tlvs = 0;
 
     arg = log_offset->lo_arg;
 
@@ -319,10 +385,62 @@ log_read_hdr_walk(struct log *log, struct log_offset 
*log_offset, const void *dp
         }
     }
 
+    if (arg->hdr->ue_flags & LOG_FLAGS_TRAILER_SUPPORT) {
+        /* Read number of TLVs, if it does not exist
+         * num_tlvs = 0, so only one TLV will get read
+         * from the end of the entry
+         */
+        rc = log_process_tlvs(log, dptr, 0, LOG_TLV_NUM_TLVS,
+                              &num_tlvs, len);
+        if (!rc || rc == SYS_ENOTSUP) {
+            arg->read_success = 1;
+        } else {
+            arg->read_success = 0;
+        }
+
+        if (!rc || rc == SYS_ENOTSUP) {
+            rc = log_process_tlvs(log, dptr, num_tlvs, LOGS_TLV_NUM_ENTRIES,
+                                  &arg->hdr->ue_num_entries, len);
+            if (!rc || rc == SYS_ENOTSUP) {
+                arg->read_success = 1;
+            } else {
+                arg->read_success = 0;
+            }
+        }
+    }
+
     /* Abort the walk; only one header needed. */
     return 1;
 }
 
+/**
+ * Update number of entries of the specified log.
+ *
+ * @param log                   The log to read from.
+ * @param num_entries           Pointer to the number of entries.
+ *
+ * @return                      0 on success; nonzero on failure.
+ */
+static int
+log_update_num_entries(struct log *log, uint32_t *num_entries)
+{
+    struct log_offset log_offset;
+    int rc = 0;
+
+
+    log_offset.lo_arg = num_entries;
+    log_offset.lo_ts = 0;
+    log_offset.lo_index = 0;
+    log_offset.lo_data_len = 0;
+
+    rc = log_walk(log, log_update_num_entries_hdr_walk, &log_offset);
+    if (rc) {
+        return -1;
+    }
+
+    return 0;
+}
+
 /**
  * Reads the final log entry's header from the specified log.
  *
@@ -354,6 +472,66 @@ log_read_last_hdr(struct log *log, struct log_entry_hdr 
*out_hdr)
     return 0;
 }
 
+/**
+ * Get number of entries in log
+ *
+ * @param log The log to get number of entries for
+ * @param idx The log index to read number of entries from
+ * @param num_entries Ptr to fill up number of entries in log
+ *
+ * @return 0 on success, non-zero on failure
+ */
+int
+log_get_entries(struct log *log, uint32_t idx, uint32_t *entries)
+{
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT) && MYNEWT_VAL(LOGS_TLV_NUM_ENTRIES)
+    int rc = 0;
+    struct log_entry_hdr hdr;
+
+    rc = log_read_hdr_by_idx(log, idx, &hdr);
+    if (!rc) {
+        *entries = log->l_num_entries - hdr.ue_num_entries;
+        return SYS_EOK;
+    } else {
+        return rc;
+    }
+#else
+    return SYS_ENOTSUP;
+#endif
+}
+
+/**
+ * Reads the log entry's header from the specified log and log index
+ *
+ * @param log                   The log to read from.
+ * @param idx                   Index of the log entry to read header from
+ * @param out_hdr               On success, the last entry header gets written
+ *                                  here.
+ *
+ * @return                      0 on success; nonzero on failure.
+ */
+int
+log_read_hdr_by_idx(struct log *log, uint32_t idx, struct log_entry_hdr 
*out_hdr)
+{
+    struct log_read_hdr_arg arg;
+    struct log_offset log_offset;
+
+    arg.hdr = out_hdr;
+    arg.read_success = 0;
+
+    log_offset.lo_arg = &arg;
+    log_offset.lo_ts = 0;
+    log_offset.lo_index = idx;
+    log_offset.lo_data_len = 0;
+
+    log_walk(log, log_read_hdr_walk, &log_offset);
+    if (!arg.read_success) {
+        return -1;
+    }
+
+    return 0;
+}
+
 /*
  * Associate an instantiation of a log with the logging infrastructure
  */
@@ -362,6 +540,7 @@ log_register(const char *name, struct log *log, const 
struct log_handler *lh,
              void *arg, uint8_t level)
 {
     struct log_entry_hdr hdr;
+    uint32_t num_entries = 0;
     int sr;
     int rc;
 
@@ -400,13 +579,21 @@ log_register(const char *name, struct log *log, const 
struct log_handler *lh,
         }
     }
 
-    /* If this is a persisted log, read the index from its most recent entry.
-     * We need to ensure the index of all subseqently written entries is
-     * monotonically increasing.
-     */
     if (log->l_log->log_type == LOG_TYPE_STORAGE) {
+        memset(&hdr, 0, sizeof(hdr));
         rc = log_read_last_hdr(log, &hdr);
         if (rc == 0) {
+            /* If the number of entries are not set in the last header,
+             * it was probably not supported when the entry was logged.
+             * Count number of entries in this specific case
+             */
+            if (!hdr.ue_num_entries) {
+                log_update_num_entries(log, &num_entries);
+            }
+            /* If this is a persisted log, read the index from its most
+             * recent entry. We need to ensure the index of all subsequently
+             * written entries is monotonically increasing.
+             */
             OS_ENTER_CRITICAL(sr);
 #if MYNEWT_VAL(LOG_GLOBAL_IDX)
             if (hdr.ue_index > g_log_info.li_next_index) {
@@ -417,6 +604,15 @@ log_register(const char *name, struct log *log, const 
struct log_handler *lh,
                 log->l_idx = hdr.ue_index;
             }
 #endif
+            /* If this is a persisted log, read the num_entries from its most
+             * recent entry. We need to ensure the number of entries are
+             * monotonically increasing.
+             */
+            if (!hdr.ue_num_entries) {
+                log->l_num_entries = num_entries;
+            } else if (hdr.ue_num_entries >= log->l_num_entries) {
+                log->l_num_entries = hdr.ue_num_entries + 1;
+            }
             OS_EXIT_CRITICAL(sr);
         }
     }
@@ -433,11 +629,55 @@ log_set_append_cb(struct log *log, log_append_cb *cb)
 uint16_t
 log_hdr_len(const struct log_entry_hdr *hdr)
 {
+    uint16_t len;
+
+    len = LOG_BASE_ENTRY_HDR_SIZE;
     if (hdr->ue_flags & LOG_FLAGS_IMG_HASH) {
-        return LOG_BASE_ENTRY_HDR_SIZE + LOG_IMG_HASHLEN;
+        len += LOG_IMG_HASHLEN;
     }
 
-    return LOG_BASE_ENTRY_HDR_SIZE;
+    return len;
+}
+
+int
+log_len_in_medium(struct log *log, uint16_t len)
+{
+    if (log->l_log->log_len_in_medium) {
+        return log->l_log->log_len_in_medium(log, len);
+    }
+
+    return len;
+}
+
+uint16_t
+log_trailer_len(struct log *log, const struct log_entry_hdr *hdr)
+{
+    if (log->l_trailer_len_cb) {
+        return log->l_trailer_len_cb(log, hdr);
+    } else {
+        return 0;
+    }
+}
+
+int
+log_trailer_append(struct log *log, uint8_t *buf, uint16_t buflen,
+                   void *loc)
+{
+    if (log->l_trailer_append_cb) {
+        return log->l_trailer_append_cb(log, buf, buflen, loc);
+    } else {
+        return SYS_ENOTSUP;
+    }
+}
+
+int
+log_mbuf_trailer_append(struct log *log, struct os_mbuf *om, void *loc)
+{
+    if (log->l_trailer_mbuf_append_cb) {
+        return log->l_trailer_mbuf_append_cb(log, om, loc);
+    } else {
+        return SYS_ENOTSUP;
+    }
 }
 
 void
@@ -565,6 +805,11 @@ log_append_prepare(struct log *log, uint8_t module, 
uint8_t level,
     }
 #endif
 
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT) && MYNEWT_VAL(LOGS_TLV_NUM_ENTRIES)
+    ue->ue_flags |= LOG_FLAGS_TRAILER_SUPPORT;
+    ue->ue_num_entries = log->l_num_entries;
+#endif
+
 err:
     return (rc);
 }
@@ -594,7 +839,7 @@ int
 log_append_typed(struct log *log, uint8_t module, uint8_t level, uint8_t etype,
                  void *data, uint16_t len)
 {
-    struct log_entry_hdr *hdr;
+    struct log_entry_hdr hdr;
     int rc;
 
     LOG_STATS_INC(log, writes);
@@ -604,20 +849,20 @@ log_append_typed(struct log *log, uint8_t module, uint8_t 
level, uint8_t etype,
         goto err;
     }
 
-    hdr = (struct log_entry_hdr *)data;
-    rc = log_append_prepare(log, module, level, etype, hdr);
+    hdr = *(struct log_entry_hdr *)data;
+    rc = log_append_prepare(log, module, level, etype, &hdr);
     if (rc != 0) {
         LOG_STATS_INC(log, drops);
         goto err;
     }
 
-    rc = log->l_log->log_append(log, data, len + log_hdr_len(hdr));
+    rc = log->l_log->log_append(log, data, len + log_hdr_len(&hdr));
     if (rc != 0) {
         LOG_STATS_INC(log, errs);
         goto err;
     }
 
-    log_call_append_cb(log, hdr->ue_index);
+    log_call_append_cb(log, hdr.ue_index);
 
     return (0);
 err:
@@ -665,7 +910,7 @@ log_append_mbuf_typed_no_free(struct log *log, uint8_t 
module, uint8_t level,
     uint16_t hdr_len;
     int rc;
 
-    /* Remove a loyer of indirection for convenience. */
+    /* Remove a layer of indirection for convenience. */
     om = *om_ptr;
 
     LOG_STATS_INC(log, writes);
@@ -932,6 +1177,20 @@ err:
     return rc;
 }
 
+/**
+ * Reads entry length from the specified log.
+ *
+ * @return                      The number of bytes of entry length; 0 on 
failure.
+ */
+uint16_t
+log_read_entry_len(struct log *log, const void *dptr)
+{
+    if (log->l_log->log_read_entry_len) {
+        return log->l_log->log_read_entry_len(log, dptr);
+    }
+    return 0;
+}
+
 /**
  * Reads from the specified log.
  *
@@ -951,7 +1210,7 @@ log_read(struct log *log, const void *dptr, void *buf, 
uint16_t off,
 int
 log_read_hdr(struct log *log, const void *dptr, struct log_entry_hdr *hdr)
 {
-    int bytes_read;
+    int bytes_read = 0;
 
     bytes_read = log_read(log, dptr, hdr, 0, LOG_BASE_ENTRY_HDR_SIZE);
     if (bytes_read != LOG_BASE_ENTRY_HDR_SIZE) {
@@ -969,6 +1228,78 @@ log_read_hdr(struct log *log, const void *dptr, struct 
log_entry_hdr *hdr)
     return 0;
 }
 
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+static int
+log_tlv_exists(uint16_t tlv)
+{
+    switch (tlv) {
+#if MYNEWT_VAL(LOGS_TLV_NUM_ENTRIES)
+    case LOGS_TLV_NUM_ENTRIES:
+        return 0;
+#endif
+
+#if MYNEWT_VAL(LOG_TLV_NUM_TLVS)
+    case LOG_TLV_NUM_TLVS:
+        return 0;
+#endif
+    default:
+        return SYS_ENOTSUP;
+    }
+}
+#endif
+
+int
+log_read_trailer(struct log *log, const void *dptr, uint16_t tlv, void *buf)
+{
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+    int rc;
+    struct log_entry_hdr hdr;
+    uint16_t entry_len;
+    uint8_t num_tlvs = 0;
+
+    if (!buf) {
+        return SYS_EINVAL;
+    }
+
+    rc = log_tlv_exists(tlv);
+    if (rc) {
+        return rc;
+    }
+
+    rc = log_read_hdr(log, dptr, &hdr);
+    if (rc) {
+        return rc;
+    }
+
+    if (hdr.ue_flags & LOG_FLAGS_TRAILER_SUPPORT) {
+        entry_len = log_read_entry_len(log, dptr);
+        if (tlv == LOG_TLV_NUM_TLVS) {
+            rc = log_process_tlvs(log, dptr, 0, LOG_TLV_NUM_TLVS, &num_tlvs,
+                                  entry_len);
+            if (rc) {
+                return rc;
+            }
+
+            memcpy(buf, &num_tlvs, LOG_NUM_TLVS_SIZE);
+        }
+
+        if (tlv == LOGS_TLV_NUM_ENTRIES) {
+            rc = log_process_tlvs(log, dptr, num_tlvs, LOGS_TLV_NUM_ENTRIES,
+                                  &hdr.ue_num_entries, entry_len);
+            if (rc) {
+                return rc;
+            }
+
+            memcpy(buf, &hdr.ue_num_entries, LOG_NUM_ENTRIES_SIZE);
+        }
+    }
+
+    return 0;
+#else
+    return SYS_ENOTSUP;
+#endif
+}
+
 int
 log_read_body(struct log *log, const void *dptr, void *buf, uint16_t off,
               uint16_t len)
@@ -1019,6 +1350,8 @@ log_flush(struct log *log)
 {
     int rc;
 
+    log->l_num_entries = 0;
+
     rc = log->l_log->log_flush(log);
     if (rc != 0) {
         goto err;
@@ -1107,6 +1440,48 @@ log_set_max_entry_len(struct log *log, uint16_t 
max_entry_len)
     log->l_max_entry_len = max_entry_len;
 }
 
+int
+log_fill_num_entries(struct log *log, const void *dptr, uint32_t *num_entries,
+                     uint16_t offset)
+{
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT) && MYNEWT_VAL(LOGS_TLV_NUM_ENTRIES)
+    int rc = 0;
+    uint32_t tmpdata;
+
+    rc = log_read(log, dptr, &tmpdata, offset, LOG_NUM_ENTRIES_SIZE);
+    if (rc < LOG_NUM_ENTRIES_SIZE) {
+        return rc;
+    }
+
+    *num_entries = tmpdata;
+
+    return SYS_EOK;
+#else
+    return SYS_ENOTSUP;
+#endif
+}
+
+int
+log_fill_num_tlvs(struct log *log, const void *dptr, uint8_t *num_tlvs,
+                  uint16_t offset)
+{
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT) && MYNEWT_VAL(LOG_TLV_NUM_TLVS)
+    int rc = 0;
+    uint32_t tmpdata;
+
+    rc = log_read(log, dptr, &tmpdata, offset, LOG_NUM_TLVS_SIZE);
+    if (rc < LOG_NUM_TLVS_SIZE) {
+        return rc;
+    }
+
+    *num_tlvs = tmpdata;
+
+    return SYS_EOK;
+#else
+    return SYS_ENOTSUP;
+#endif
+}
+
 int
 log_fill_current_img_hash(struct log_entry_hdr *hdr)
 {
diff --git a/sys/log/full/src/log_cbmem.c b/sys/log/full/src/log_cbmem.c
index 02cd3503f..d43d79912 100644
--- a/sys/log/full/src/log_cbmem.c
+++ b/sys/log/full/src/log_cbmem.c
@@ -25,8 +25,14 @@ static int
 log_cbmem_append_body(struct log *log, const struct log_entry_hdr *hdr,
                       const void *body, int body_len)
 {
+    int rc = 0;
     struct cbmem *cbmem;
-    struct cbmem_scat_gath sg = {
+    uint8_t trailer[LOG_NUM_ENTRIES_SIZE] = {0};
+    uint16_t trailer_len = 0;
+    struct cbmem_scat_gath *sg = (struct cbmem_scat_gath *)&trailer[0];
+
+    trailer_len = log_trailer_len(log, hdr);
+    *sg = (struct cbmem_scat_gath) {
         .entries = (struct cbmem_scat_gath_entry[]) {
             {
                 .flat_buf = hdr,
@@ -40,21 +46,40 @@ log_cbmem_append_body(struct log *log, const struct 
log_entry_hdr *hdr,
                 .flat_buf = body,
                 .flat_len = body_len,
             },
+            {
+                .flat_buf = trailer,
+                .flat_len = 0
+            }
         },
-        .count = 3,
+        .count = 4,
     };
 
     if (hdr->ue_flags & LOG_FLAGS_IMG_HASH) {
-        sg.entries[1].flat_len = LOG_IMG_HASHLEN;
+        sg->entries[1].flat_len = LOG_IMG_HASHLEN;
+    }
+
+    if (hdr->ue_flags & LOG_FLAGS_TRAILER_SUPPORT) {
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+        if (log->l_cbmem_trailer_append_cb) {
+            rc = log->l_cbmem_trailer_append_cb(log, trailer, trailer_len);
+            if (!rc) {
+                sg->count += ((trailer_len - 
sizeof(sg->count))/sizeof(sg->entries[0]));
+                sg->entries[3].flat_len = trailer_len;
+            }
+        }
+#endif
     }
+
     cbmem = (struct cbmem *) log->l_arg;
 
-    return cbmem_append_scat_gath(cbmem, &sg);
+    rc = cbmem_append_scat_gath(cbmem, sg);
+
+    return rc;
 }
 
 static int
 log_cbmem_append(struct log *log, void *buf, int len)
-{   
+{
     uint16_t hdr_len;
 
     hdr_len = log_hdr_len(buf);
@@ -67,6 +92,7 @@ static int
 log_cbmem_append_mbuf_body(struct log *log, const struct log_entry_hdr *hdr,
                            struct os_mbuf *om)
 {
+    int rc = 0;
     struct cbmem *cbmem;
     struct cbmem_scat_gath sg = {
         .entries = (struct cbmem_scat_gath_entry[]) {
@@ -88,9 +114,20 @@ log_cbmem_append_mbuf_body(struct log *log, const struct 
log_entry_hdr *hdr,
     if (hdr->ue_flags & LOG_FLAGS_IMG_HASH) {
         sg.entries[1].flat_len = LOG_IMG_HASHLEN;
     }
+
+    if (hdr->ue_flags & LOG_FLAGS_TRAILER_SUPPORT) {
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+        if (log->l_cbmem_trailer_mbuf_append_cb) {
+            log->l_cbmem_trailer_mbuf_append_cb(log, om);
+        }
+#endif
+    }
+
     cbmem = (struct cbmem *) log->l_arg;
 
-    return cbmem_append_scat_gath(cbmem, &sg);
+    rc = cbmem_append_scat_gath(cbmem, &sg);
+
+    return rc;
 }
 
 static int
@@ -110,25 +147,25 @@ log_cbmem_append_mbuf(struct log *log, struct os_mbuf *om)
      * We do a pull up twice, once so that the base header is
      * contiguous, so that we read the flags correctly, second
      * time is so that we account for the image hash as well.
-     */    
+     */
 
     os_mbuf_pullup(om, LOG_BASE_ENTRY_HDR_SIZE);
-   
-    /* 
+
+    /*
      * We can just pass the om->om_data ptr as the log_entry_hdr
      * because the log_entry_hdr is a packed struct and does not
      * cause any alignment or padding issues
-     */  
+     */
     hdr_len = log_hdr_len((struct log_entry_hdr *)om->om_data);
-    
+
     os_mbuf_pullup(om, hdr_len);
-    
+
     memcpy(&hdr, om->om_data, hdr_len);
 
     os_mbuf_adj(om, hdr_len);
 
     rc = log_cbmem_append_mbuf_body(log, &hdr, om);
-    
+
     os_mbuf_prepend(om, hdr_len);
 
     memcpy(om->om_data, &hdr, hdr_len);
@@ -136,6 +173,16 @@ log_cbmem_append_mbuf(struct log *log, struct os_mbuf *om)
     return rc;
 }
 
+static uint16_t
+log_cbmem_read_entry_len(struct log *log, const void *dptr)
+{
+    struct cbmem_entry_hdr *hdr;
+
+    hdr = (struct cbmem_entry_hdr *) dptr;
+
+    return hdr->ceh_len;
+}
+
 static int
 log_cbmem_read(struct log *log, const void *dptr, void *buf, uint16_t offset,
         uint16_t len)
@@ -264,6 +311,7 @@ log_cbmem_storage_info(struct log *log, struct 
log_storage_info *info)
 const struct log_handler log_cbmem_handler = {
     .log_type = LOG_TYPE_MEMORY,
     .log_read = log_cbmem_read,
+    .log_read_entry_len = log_cbmem_read_entry_len,
     .log_read_mbuf = log_cbmem_read_mbuf,
     .log_append = log_cbmem_append,
     .log_append_body = log_cbmem_append_body,
diff --git a/sys/log/full/src/log_fcb.c b/sys/log/full/src/log_fcb.c
index 529e8f473..66db2fcf1 100644
--- a/sys/log/full/src/log_fcb.c
+++ b/sys/log/full/src/log_fcb.c
@@ -26,6 +26,7 @@
 #include "flash_map/flash_map.h"
 #include "fcb/fcb.h"
 #include "log/log.h"
+#include <console/console.h>
 
 /* Assume the flash alignment requirement is no stricter than 32. */
 #define LOG_FCB_MAX_ALIGN   32
@@ -316,20 +317,33 @@ err:
     return (rc);
 }
 
-/**
- * Calculates the number of message body bytes that should be included after
- * the entry header in the first write.  Inclusion of body bytes is necessary
- * to satisfy the flash hardware's write alignment restrictions.
- */
 static int
-log_fcb_hdr_body_bytes(uint8_t align, uint8_t hdr_len)
+log_fcb_write_mbuf(struct fcb_entry *loc, struct os_mbuf *om)
 {
-    uint8_t mod;
+    int rc;
+
+    while (om) {
+        rc = flash_area_write(loc->fe_area, loc->fe_data_off, om->om_data,
+                              om->om_len);
+        if (rc != 0) {
+            return SYS_EIO;
+        }
+        loc->fe_data_off += om->om_len;
+        om = SLIST_NEXT(om, om_next);
+    }
+
+    return 0;
+}
+
+int
+log_fcb_hdr_trailer_bytes(uint16_t align, uint16_t len)
+{
+    uint16_t mod;
 
     /* Assume power-of-two alignment for faster modulo calculation. */
     assert((align & (align - 1)) == 0);
 
-    mod = hdr_len & (align - 1);
+    mod = len & (align - 1);
     if (mod == 0) {
         return 0;
     }
@@ -341,27 +355,35 @@ static int
 log_fcb_append_body(struct log *log, const struct log_entry_hdr *hdr,
                     const void *body, int body_len)
 {
-    uint8_t buf[LOG_BASE_ENTRY_HDR_SIZE + LOG_IMG_HASHLEN +
-                LOG_FCB_MAX_ALIGN - 1];
+    uint8_t buf[LOG_FCB_FLAT_BUF_SIZE] = {0};
     struct fcb *fcb;
     struct fcb_entry loc = {};
     struct fcb_log *fcb_log;
     const uint8_t *u8p;
     int hdr_alignment;
-    int chunk_sz;
+    int trailer_alignment = 0;
+    int chunk_sz = 0;
     int rc;
     uint16_t hdr_len;
+    uint16_t trailer_len;
+    uint16_t padding = 0;
+    uint16_t offset = 0;
 
     fcb_log = (struct fcb_log *)log->l_arg;
     fcb = &fcb_log->fl_fcb;
 
+    (void)offset;
+    (void)padding;
+    (void)trailer_alignment;
+
     if (fcb->f_align > LOG_FCB_MAX_ALIGN) {
         return SYS_ENOTSUP;
     }
 
     hdr_len = log_hdr_len(hdr);
+    trailer_len = log_trailer_len(log, hdr);
 
-    rc = log_fcb_start_append(log, hdr_len + body_len, &loc);
+    rc = log_fcb_start_append(log, hdr_len + body_len + trailer_len, &loc);
     if (rc != 0) {
         return rc;
     }
@@ -371,7 +393,7 @@ log_fcb_append_body(struct log *log, const struct 
log_entry_hdr *hdr,
      * the flash alignment). If the hash flag is set, we have to account for
      * appending the hash right after the header.
      */
-    hdr_alignment = log_fcb_hdr_body_bytes(fcb->f_align, hdr_len);
+    hdr_alignment = log_fcb_hdr_trailer_bytes(fcb->f_align, hdr_len);
     if (hdr_alignment > body_len) {
         chunk_sz = hdr_len + body_len;
     } else {
@@ -389,6 +411,7 @@ log_fcb_append_body(struct log *log, const struct 
log_entry_hdr *hdr,
     if (hdr->ue_flags & LOG_FLAGS_IMG_HASH) {
         memcpy(buf + LOG_BASE_ENTRY_HDR_SIZE, hdr->ue_imghash, 
LOG_IMG_HASHLEN);
     }
+
     memcpy(buf + hdr_len, u8p, hdr_alignment);
 
     rc = fcb_write(fcb, &loc, buf, chunk_sz);
@@ -396,15 +419,70 @@ log_fcb_append_body(struct log *log, const struct 
log_entry_hdr *hdr,
         return rc;
     }
 
+    loc.fe_data_off += chunk_sz;
+
     /* Append the remainder of the message body. */
 
     u8p += hdr_alignment;
     body_len -= hdr_alignment;
 
-    if (body_len > 0) {
-        rc = fcb_write(fcb, &loc, u8p, body_len);
-        if (rc != 0) {
-            return rc;
+    if (hdr->ue_flags & LOG_FLAGS_TRAILER_SUPPORT) {
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+        /* This writes padding + trailer_alignment */
+
+        /* Calculate trailer alignment */
+        trailer_alignment = log_fcb_hdr_trailer_bytes(fcb->f_align, chunk_sz + 
body_len);
+
+        if (body_len > 0) {
+            padding = trailer_alignment ? fcb->f_align - trailer_alignment : 0;
+            /* Writes body - padding bytes */
+            rc = flash_area_write(loc.fe_area, loc.fe_data_off, u8p,
+                                  body_len - padding);
+            if (rc != 0) {
+                return rc;
+            }
+
+            loc.fe_data_off += body_len - padding;
+
+            u8p = u8p + body_len - padding;
+            memset(buf, 0, sizeof(buf));
+            memcpy(buf, u8p, padding);
+            offset = padding;
+            offset += trailer_alignment;
+            /* Writes the following:
+             * 
-----------------------------------------------------------------
+             * | body: body_len - padding from end of body | trailer_alignment 
|
+             * 
-----------------------------------------------------------------
+             */
+            rc = flash_area_write(loc.fe_area, loc.fe_data_off, buf, offset);
+            if (rc != 0) {
+                return rc;
+            }
+
+            loc.fe_data_off += offset;
+
+            /* The first trailer gets appended after the padding + 
trailer_alignment
+             * Trailers start from updated loc.fe_data_off.
+             */
+            rc = log_trailer_append(log, buf, trailer_len, &loc);
+            if (rc && rc != SYS_ENOTSUP) {
+                return rc;
+            }
+        }
+#else
+        if (body_len > 0) {
+            rc = flash_area_write(loc.fe_area, loc.fe_data_off, u8p, body_len);
+            if (rc != 0) {
+                return rc;
+            }
+        }
+#endif
+    } else {
+        if (body_len > 0) {
+            rc = flash_area_write(loc.fe_area, loc.fe_data_off, u8p, body_len);
+            if (rc != 0) {
+                return rc;
+            }
         }
     }
 
@@ -427,25 +505,6 @@ log_fcb_append(struct log *log, void *buf, int len)
                                len - hdr_len);
 }
 
-static int
-log_fcb_write_mbuf(struct fcb_entry *loc, struct os_mbuf *om)
-{
-    int rc;
-
-    while (om) {
-        rc = flash_area_write(loc->fe_area, loc->fe_data_off, om->om_data,
-                              om->om_len);
-        if (rc != 0) {
-            return SYS_EIO;
-        }
-
-        loc->fe_data_off += om->om_len;
-        om = SLIST_NEXT(om, om_next);
-    }
-
-    return 0;
-}
-
 static int
 log_fcb_append_mbuf_body(struct log *log, const struct log_entry_hdr *hdr,
                          struct os_mbuf *om)
@@ -466,7 +525,7 @@ log_fcb_append_mbuf_body(struct log *log, const struct 
log_entry_hdr *hdr,
         return SYS_ENOTSUP;
     }
 
-    len = log_hdr_len(hdr) + os_mbuf_len(om);
+    len = log_hdr_len(hdr) + os_mbuf_len(om) + log_trailer_len(log, hdr);
     rc = log_fcb_start_append(log, len, &loc);
     if (rc != 0) {
         return rc;
@@ -488,9 +547,23 @@ log_fcb_append_mbuf_body(struct log *log, const struct 
log_entry_hdr *hdr,
         }
         loc.fe_data_off += LOG_IMG_HASHLEN;
     }
-    rc = log_fcb_write_mbuf(&loc, om);
-    if (rc != 0) {
-        return rc;
+
+    if (hdr->ue_flags & LOG_FLAGS_TRAILER_SUPPORT) {
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+        /* The first TLV gets appended after the padding + trailer_alignment
+         * Trailers start from updated loc.fe_data_off. Write everything
+         * together
+         */
+        rc = log_mbuf_trailer_append(log, om, &loc);
+        if (rc && rc != SYS_ENOTSUP) {
+            return rc;
+        }
+#endif
+    } else {
+        rc = log_fcb_write_mbuf(&loc, om);
+        if (rc != 0) {
+            return rc;
+        }
     }
 
     rc = fcb_append_finish(fcb, &loc);
@@ -543,6 +616,20 @@ log_fcb_append_mbuf(struct log *log, struct os_mbuf *om)
     return rc;
 }
 
+static uint16_t
+log_fcb_read_entry_len(struct log *log, const void *dptr)
+{
+    struct fcb_entry *loc;
+
+    loc = (struct fcb_entry *)dptr;
+
+    if (!log || !dptr) {
+        return 0;
+    }
+
+    return loc->fe_data_len;
+}
+
 static int
 log_fcb_read(struct log *log, const void *dptr, void *buf, uint16_t offset,
   uint16_t len)
@@ -844,6 +931,18 @@ log_fcb_new_watermark_index(struct log *log, struct 
log_offset *log_offset,
     }
 }
 
+static int
+log_fcb_len_in_medium(struct log *log, uint16_t len)
+{
+    struct fcb_log *fl;
+    struct fcb *fcb;
+
+    fl = (struct fcb_log *)log->l_arg;
+    fcb = &fl->fl_fcb;
+
+    return fcb_len_in_flash(fcb, len);
+}
+
 static int
 log_fcb_set_watermark(struct log *log, uint32_t index)
 {
@@ -891,8 +990,8 @@ log_fcb_copy_entry(struct log *log, struct fcb_entry *entry,
                    struct fcb *dst_fcb)
 {
     struct log_entry_hdr ueh;
-    char data[MYNEWT_VAL(LOG_FCB_COPY_MAX_ENTRY_LEN) + LOG_BASE_ENTRY_HDR_SIZE 
+
-              LOG_IMG_HASHLEN];
+    char data[MYNEWT_VAL(LOG_FCB_COPY_MAX_ENTRY_LEN) +
+              LOG_BASE_ENTRY_HDR_SIZE + LOG_IMG_HASHLEN];
     uint16_t hdr_len;
     int dlen;
     int rc;
@@ -905,7 +1004,6 @@ log_fcb_copy_entry(struct log *log, struct fcb_entry 
*entry,
     }
 
     hdr_len = log_hdr_len(&ueh);
-
     dlen = min(entry->fe_data_len, MYNEWT_VAL(LOG_FCB_COPY_MAX_ENTRY_LEN) +
                hdr_len);
 
@@ -1031,23 +1129,25 @@ err:
 }
 
 const struct log_handler log_fcb_handler = {
-    .log_type             = LOG_TYPE_STORAGE,
-    .log_read             = log_fcb_read,
-    .log_read_mbuf        = log_fcb_read_mbuf,
-    .log_append           = log_fcb_append,
-    .log_append_body      = log_fcb_append_body,
-    .log_append_mbuf      = log_fcb_append_mbuf,
+    .log_type = LOG_TYPE_STORAGE,
+    .log_read = log_fcb_read,
+    .log_read_mbuf = log_fcb_read_mbuf,
+    .log_append = log_fcb_append,
+    .log_append_body = log_fcb_append_body,
+    .log_append_mbuf = log_fcb_append_mbuf,
     .log_append_mbuf_body = log_fcb_append_mbuf_body,
-    .log_walk             = log_fcb_walk,
-    .log_walk_sector      = log_fcb_walk_area,
-    .log_flush            = log_fcb_flush,
+    .log_walk = log_fcb_walk,
+    .log_walk_sector = log_fcb_walk_area,
+    .log_flush = log_fcb_flush,
+    .log_read_entry_len = log_fcb_read_entry_len,
 #if MYNEWT_VAL(LOG_STORAGE_INFO)
-    .log_storage_info     = log_fcb_storage_info,
+    .log_storage_info = log_fcb_storage_info,
 #endif
 #if MYNEWT_VAL(LOG_STORAGE_WATERMARK)
-    .log_set_watermark    = log_fcb_set_watermark,
+    .log_set_watermark = log_fcb_set_watermark,
+    .log_len_in_medium = log_fcb_len_in_medium,
 #endif
-    .log_registered       = log_fcb_registered,
+    .log_registered = log_fcb_registered,
 };
 
 #endif
diff --git a/sys/log/full/src/log_fcb2.c b/sys/log/full/src/log_fcb2.c
index 2bb2e604e..aca21e402 100644
--- a/sys/log/full/src/log_fcb2.c
+++ b/sys/log/full/src/log_fcb2.c
@@ -206,14 +206,14 @@ err:
  * to satisfy the flash hardware's write alignment restrictions.
  */
 static int
-log_fcb2_hdr_body_bytes(uint8_t align, uint8_t hdr_len)
+log_fcb2_hdr_body_bytes(uint16_t align, uint16_t len)
 {
-    uint8_t mod;
+    uint16_t mod;
 
     /* Assume power-of-two alignment for faster modulo calculation. */
     assert((align & (align - 1)) == 0);
 
-    mod = hdr_len & (align - 1);
+    mod = len & (align - 1);
     if (mod == 0) {
         return 0;
     }
@@ -221,22 +221,44 @@ log_fcb2_hdr_body_bytes(uint8_t align, uint8_t hdr_len)
     return align - mod;
 }
 
+static int
+log_fcb2_write_mbuf(struct fcb2_entry *loc, struct os_mbuf *om, int off)
+{
+    int rc;
+
+    while (om) {
+        rc = fcb2_write(loc, off, om->om_data, om->om_len);
+        if (rc != 0) {
+            return SYS_EIO;
+        }
+
+        off += om->om_len;
+        om = SLIST_NEXT(om, om_next);
+    }
+
+    return SYS_EOK;
+}
+
 static int
 log_fcb2_append_body(struct log *log, const struct log_entry_hdr *hdr,
                      const void *body, int body_len)
 {
-    uint8_t buf[LOG_BASE_ENTRY_HDR_SIZE + LOG_IMG_HASHLEN +
-                LOG_FCB2_MAX_ALIGN - 1];
+    uint8_t buf[LOG_FCB_FLAT_BUF_SIZE] = {0};
     struct fcb2_entry loc;
     const uint8_t *u8p;
     int hdr_alignment;
-    int chunk_sz;
+    int trailer_alignment = 0;
+    uint16_t chunk_sz = 0;
     int rc;
     uint16_t hdr_len;
+    uint16_t trailer_len;
 
     hdr_len = log_hdr_len(hdr);
+    trailer_len = log_trailer_len(log, hdr);
 
-    rc = log_fcb2_start_append(log, hdr_len + body_len, &loc);
+    (void)trailer_alignment;
+
+    rc = log_fcb2_start_append(log, hdr_len + body_len + trailer_len, &loc);
     if (rc != 0) {
         return rc;
     }
@@ -275,11 +297,63 @@ log_fcb2_append_body(struct log *log, const struct 
log_entry_hdr *hdr,
 
     u8p += hdr_alignment;
     body_len -= hdr_alignment;
+    if (hdr->ue_flags & LOG_FLAGS_TRAILER_SUPPORT) {
+        memset(buf, 0, sizeof(buf));
+        /* Calculate trailer alignment */
+        trailer_alignment = log_fcb2_hdr_body_bytes(loc.fe_range->fsr_align, 
chunk_sz + body_len);
+
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+        uint16_t offset = 0;
+        uint16_t padding = 0;
+
+        if (body_len > 0) {
+            padding = trailer_alignment ? loc.fe_range->fsr_align - 
trailer_alignment : 0;
+            rc = fcb2_write(&loc, chunk_sz, u8p, body_len - padding);
+            if (rc != 0) {
+                return rc;
+            }
 
-    if (body_len > 0) {
-        rc = fcb2_write(&loc, chunk_sz, u8p, body_len);
-        if (rc != 0) {
-            return rc;
+            chunk_sz += body_len - padding;
+
+            u8p = u8p + body_len - padding;
+            memcpy(buf, u8p, padding);
+            offset = padding;
+            offset += trailer_alignment;
+            /* Writes the following:
+             * 
-----------------------------------------------------------------
+             * | body: body_len - padding from end of body | trailer_alignment 
|
+             * 
-----------------------------------------------------------------
+             */
+            rc = fcb2_write(&loc, chunk_sz, buf, offset);
+            if (rc != 0) {
+                return rc;
+            }
+
+            chunk_sz += offset;
+            /* The first TLV gets appended after the padding + 
trailer_alignment
+             * Trailers start from updated chunk_sz offset.
+             */
+            rc = log_fcb2_tlvs_write(log, buf, sizeof(buf), &loc, &chunk_sz);
+            if (rc) {
+                return rc;
+            }
+        }
+#else
+        if (body_len > 0) {
+            rc = fcb2_write(&loc, chunk_sz, u8p, body_len);
+            if (rc != 0) {
+                return rc;
+            }
+            chunk_sz += body_len;
+        }
+#endif
+    } else {
+        if (body_len > 0) {
+            rc = fcb2_write(&loc, chunk_sz, u8p, body_len);
+            if (rc != 0) {
+                return rc;
+            }
+            chunk_sz += body_len;
         }
     }
 
@@ -302,24 +376,6 @@ log_fcb2_append(struct log *log, void *buf, int len)
                                 len - hdr_len);
 }
 
-static int
-log_fcb2_write_mbuf(struct fcb2_entry *loc, struct os_mbuf *om, int off)
-{
-    int rc;
-
-    while (om) {
-        rc = fcb2_write(loc, off, om->om_data, om->om_len);
-        if (rc != 0) {
-            return SYS_EIO;
-        }
-
-        off += om->om_len;
-        om = SLIST_NEXT(om, om_next);
-    }
-
-    return 0;
-}
-
 static int
 log_fcb2_append_mbuf_body(struct log *log, const struct log_entry_hdr *hdr,
                           struct os_mbuf *om)
@@ -340,7 +396,7 @@ log_fcb2_append_mbuf_body(struct log *log, const struct 
log_entry_hdr *hdr,
     }
 #endif
 
-    len = log_hdr_len(hdr) + os_mbuf_len(om);
+    len = log_hdr_len(hdr) + os_mbuf_len(om) + log_trailer_len(log, hdr);
     rc = log_fcb2_start_append(log, len, &loc);
     if (rc != 0) {
         return rc;
@@ -360,9 +416,23 @@ log_fcb2_append_mbuf_body(struct log *log, const struct 
log_entry_hdr *hdr,
         }
         len += LOG_IMG_HASHLEN;
     }
-    rc = log_fcb2_write_mbuf(&loc, om, len);
-    if (rc != 0) {
-        return rc;
+
+    if (hdr->ue_flags & LOG_FLAGS_TRAILER_SUPPORT) {
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+        /* The first TLV gets appended after the padding + trailer_alignment
+         * Trailers start from updated loc.fe_data_off. Write everything
+         * together
+         */
+        rc = log_fcb2_mbuf_tlvs_write(log, om, &loc, len);
+        if (rc != 0) {
+            return rc;
+        }
+#endif
+    } else {
+        rc = log_fcb2_write_mbuf(&loc, om, len);
+        if (rc != 0) {
+            return rc;
+        }
     }
 
     rc = fcb2_append_finish(&loc);
@@ -373,6 +443,18 @@ log_fcb2_append_mbuf_body(struct log *log, const struct 
log_entry_hdr *hdr,
     return 0;
 }
 
+static int
+log_fcb2_len_in_medium(struct log *log, uint16_t len)
+{
+    struct fcb_log *fl;
+    struct fcb2 *fcb;
+
+    fl = (struct fcb_log *)log->l_arg;
+    fcb = &fl->fl_fcb;
+
+    return fcb2_len_in_flash(fcb->f_active.fe_range, len);
+}
+
 static int
 log_fcb2_append_mbuf(struct log *log, struct os_mbuf *om)
 {
@@ -415,6 +497,20 @@ log_fcb2_append_mbuf(struct log *log, struct os_mbuf *om)
     return rc;
 }
 
+static uint16_t
+log_fcb2_read_entry_len(struct log *log, const void *dptr)
+{
+    struct fcb2_entry *loc;
+
+    loc = (struct fcb2_entry *)dptr;
+
+    if (!log || !dptr) {
+        return 0;
+    }
+
+    return loc->fe_data_len;
+}
+
 static int
 log_fcb2_read(struct log *log, const void *dptr, void *buf, uint16_t off, 
uint16_t len)
 {
@@ -901,6 +997,8 @@ const struct log_handler log_fcb_handler = {
     .log_append_mbuf_body = log_fcb2_append_mbuf_body,
     .log_walk = log_fcb2_walk,
     .log_flush = log_fcb2_flush,
+    .log_read_entry_len = log_fcb2_read_entry_len,
+    .log_len_in_medium = log_fcb2_len_in_medium,
 #if MYNEWT_VAL(LOG_STORAGE_INFO)
     .log_storage_info = log_fcb2_storage_info,
 #endif
diff --git a/sys/log/full/src/log_shell.c b/sys/log/full/src/log_shell.c
index c33be9eae..0d0285e99 100644
--- a/sys/log/full/src/log_shell.c
+++ b/sys/log/full/src/log_shell.c
@@ -90,6 +90,15 @@ shell_log_dump_entry(struct log *log, struct log_offset 
*log_offset,
     bool read_hash = ueh->ue_flags & LOG_FLAGS_IMG_HASH;
     bool add_lf = true;
 
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT) && MYNEWT_VAL(LOGS_TLV_NUM_ENTRIES)
+    bool read_num_entries = ueh->ue_flags & LOG_FLAGS_TRAILER_SUPPORT;
+#else
+    bool read_num_entries = false;
+#endif
+    uint32_t entries = 0;
+
+    dlen = min(len, 128);
+
     if (arg) {
         arg->count++;
         /* Continue walk if number of entries to skip not reached yet */
@@ -98,9 +107,17 @@ shell_log_dump_entry(struct log *log, struct log_offset 
*log_offset,
         }
     }
 
-    dlen = min(len, 128);
-
     if (read_data) {
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+        dlen -= log_len_in_medium(log, sizeof(struct log_tlv));
+
+        rc = log_read_trailer(log, dptr, LOGS_TLV_NUM_ENTRIES, &entries);
+        if (!rc) {
+            dlen -= log_len_in_medium(log, LOG_NUM_ENTRIES_SIZE);
+        } else {
+            console_printf("failed to read trailer\n");
+        }
+#endif
         rc = log_read_body(log, dptr, data, 0, dlen);
         if (rc < 0) {
             return rc;
@@ -120,6 +137,17 @@ shell_log_dump_entry(struct log *log, struct log_offset 
*log_offset,
         console_printf(" [%llu] ", ueh->ue_ts);
     }
 
+    if (read_num_entries) {
+#if MYNEWT_VAL(LOG_FLAGS_TRAILER_SUPPORT)
+        dlen -= log_len_in_medium(log, sizeof(struct log_tlv));
+        rc = log_read_trailer(log, dptr, LOGS_TLV_NUM_ENTRIES, &entries);
+        if (!rc) {
+            dlen -= log_len_in_medium(log, LOG_NUM_ENTRIES_SIZE);
+        }
+#endif
+        console_printf("[ne=%u]", (unsigned int)entries);
+    }
+    console_printf(" [%llu] ", ueh->ue_ts);
 #if MYNEWT_VAL(LOG_SHELL_SHOW_INDEX)
     console_printf(" [ix=%lu] ", ueh->ue_index);
 #endif
@@ -178,6 +206,8 @@ shell_log_dump_cmd(int argc, char **argv)
     uint32_t bmarks_size = 0;
     struct log_fcb_bmark *bmarks = NULL;
     struct walk_arg arg = {};
+    bool num_entries = false;
+    uint32_t entries;
     int i;
     int rc = 0;
     int start = -1;
@@ -245,6 +275,16 @@ shell_log_dump_cmd(int argc, char **argv)
         /* the -c option is to clear a log (or logs). */
         if (!strcmp(argv[i], "-c")) {
             clear_log = true;
+        } else if (argc == 3 && !strcmp(argv[i], "-ne")) {
+            num_entries = true;
+            log_name = argv[i+1];
+            break;
+        } else if (argc == 5 && !strcmp(argv[i], "-ne") &&
+                   !strcmp(argv[i+2], "-i")) {
+            num_entries = true;
+            log_name = argv[i+1];
+            log_limit = parse_ll_bounds(argv[i+3], 1, 1000000, &rc);
+            break;
         } else if (isdigit((unsigned char)argv[i][0])) {
             log_limit = parse_ll_bounds(argv[i], 1, 1000000, &rc);
             if (clear_log) {
@@ -337,6 +377,13 @@ shell_log_dump_cmd(int argc, char **argv)
             if (rc != 0) {
                 goto err;
             }
+        } else if (num_entries) {
+            rc = log_get_entries(log, log_limit, &entries);
+            if (!rc) {
+                console_printf("entries: %u\n", (unsigned int)entries);
+            } else {
+                console_printf("Invalid or empty log, rc=%d!\n", rc);
+            }
         } else {
             if (dump_logs) {
                 console_printf("Dumping log %s\n", log->l_name);
diff --git a/sys/log/full/syscfg.yml b/sys/log/full/syscfg.yml
index 7170e91ed..00f65d5a6 100644
--- a/sys/log/full/syscfg.yml
+++ b/sys/log/full/syscfg.yml
@@ -58,6 +58,12 @@ syscfg.defs:
             1 - enable.
         value: 0
 
+    LOG_FLAGS_TRAILER_SUPPORT:
+        description: >
+            Enable logging TLV with custom data types in every log entry
+            0 - disable; 1 - enable.
+        value: 0
+
     LOG_FCB:
         description: 'Support logging to FCB.'
         value: 0

Reply via email to