diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,14 +3,94 @@
 `co-log` uses [PVP Versioning][1].
 The changelog is available [on GitHub][2].
 
+## 0.7.0.0 - April 6, 2025
+
+## What's Changed
+
+* Move away from `chronos`. (#277)
+
+  **Breaking change**: Some signatures have changed from `Chronos.Time` to `Data.Time.UTCTime`.
+
+**Full Changelog**: https://github.com/co-log/co-log/compare/v0.6.1.2...v0.7.0.0
+
+## 0.6.1.2 - March 2, 2025
+
+## What's Changed
+
+* Allow `containers-0.8`.
+
+**Full Changelog**: https://github.com/co-log/co-log/compare/v0.6.1.1...v0.6.1.2
+
+## 0.6.1.1 - January 5, 2025
+
+## What's Changed
+
+* Support ghc-9.10 & ghc-9.12.
+
+**Full Changelog**: https://github.com/co-log/co-log/compare/v0.6.1.0...v0.6.1.1
+
+## 0.6.1.0 - Mar 1, 2024
+
+## What's Changed
+
+* GA(deps): Bump actions/cache from 3 to 4 by @dependabot in https://github.com/co-log/co-log/pull/273
+* docs: refine the loggert tutorials by @xieyuschen in https://github.com/co-log/co-log/pull/272
+* Support ghc-9.8. by @alaendle in https://github.com/co-log/co-log/pull/270
+
+**Full Changelog**: https://github.com/co-log/co-log/compare/v0.6.0.2...v0.6.1.0
+
+## 0.6.0.0 - Sep 18, 2023
+
+### What's Changed
+
+* Support GHC-9.6 - replace `typerep-map` with `dependent-map`. by @alaendle in [#264](https://github.com/co-log/co-log/pull/264)
+* Support GHC 9.4. by @alaendle in [#252](https://github.com/co-log/co-log/pull/252)
+* Add MonadUnliftIO instance by @newhoggy in [#240](https://github.com/co-log/co-log/pull/240)
+* Update CI tested GHC versions & workaround for GHC < 9.4.5 (run-st, primitive-unlifted) by @alaendle in [#257](https://github.com/co-log/co-log/pull/257)
+* Derive `MonadFail` for `LoggerT` by @alaendle in [#260](https://github.com/co-log/co-log/pull/260)
+* docs: use relative path to benefit locally reading by @xieyuschen in [#253](https://github.com/co-log/co-log/pull/253)
+* docs: refine readme by @xieyuschen in [#254](https://github.com/co-log/co-log/pull/254)
+* tutorials: add demo for LoggerT and SimpleMsg, #84 by @xieyuschen in [#256](https://github.com/co-log/co-log/pull/256)
+* docs: add a link to tutorial pages and aggregate all tutorial pages by @xieyuschen in [#259](https://github.com/co-log/co-log/pull/259)
+* tutorials: add a tutorial for loggert and message by @xieyuschen in [#261](https://github.com/co-log/co-log/pull/261)
+* Create tags and upload package candidates on version bumps. by @alaendle in [#265](https://github.com/co-log/co-log/pull/265)
+* Added @alaendle as code owner. by @alaendle in [#258](https://github.com/co-log/co-log/pull/258)
+* GA(deps): Bump actions/checkout from 3 to 4 by @dependabot in [#263](https://github.com/co-log/co-log/pull/263)
+
+### New Contributors
+
+* @xieyuschen made their first contribution in [#253](https://github.com/co-log/co-log/pull/253)
+* @newhoggy made their first contribution in [#240](https://github.com/co-log/co-log/pull/240)
+
+**Full Changelog**: https://github.com/co-log/co-log/compare/v0.5.0.0...v0.6.0.0
+
+## 0.5.0.0 - Nov 2, 2022
+
+* [#230](https://github.com/co-log/co-log/issues/230):
+  Support GHC-9.2.
+* Allow `mtl-2.3`.
+* Allow `vector-0.13`.
+* Allow `hedgehog-1.2`.
+* [#187](https://github.com/co-log/co-log/issues/187):
+  Remove `CoLog.Concurrent` module and executable.
+* [#243](https://github.com/co-log/co-log/pull/243):
+  Improve printing in multiple threads.
+* Drop support for GHC-8.2, GHC-8.4, GHC-8.6, GHC-8.8
+
+## 0.4.0.2 — <M> <d>, 2021
+
+* [#223](https://github.com/co-log/co-log/pulls/223):
+  Support GHC-9.0.1.
+  Require typerep-map ^>= 0.4
+
 ## 0.4.0.1 — Apr 18, 2020
 
-* [#186](https://github.com/kowainik/co-log/issues/186):
+* [#186](https://github.com/co-log/co-log/issues/186):
   Support GHC-8.10.1.
 
 ## 0.4.0.0 — Jan 19, 2020
 
-* [#120](https://github.com/kowainik/co-log/issues/120):
+* [#120](https://github.com/co-log/co-log/issues/120):
   Improve time formatting.
 
   Old: `29-12-2019 22:00:00.000`
@@ -18,39 +98,39 @@
   New: `29 Dec 2019 22:00:00.000 +00:00`
 
   (by [@vrom911](https://github.com/vrom911))
-* [#144](https://github.com/kowainik/co-log/issues/144):
+* [#144](https://github.com/co-log/co-log/issues/144):
   Add Windows CI check.
   (by [@vrom911](https://github.com/vrom911))
-* [#148](https://github.com/kowainik/co-log/issues/148):
+* [#148](https://github.com/co-log/co-log/issues/148):
   Support GHC-8.8.2.
   (by [@chshersh](https://github.com/chshersh))
-* [#119](https://github.com/kowainik/co-log/issues/119):
+* [#119](https://github.com/co-log/co-log/issues/119):
   Add new message type that allows printing messages without
   `Severity`.
   (by [@sphaso](https://github.com/sphaso))
-* [#150](https://github.com/kowainik/co-log/issues/150):
+* [#150](https://github.com/co-log/co-log/issues/150):
   Introduce `formatWith` — beginner-friendly alias for formatting combinator.
   (by [@chshersh](https://github.com/chshersh))
 * Use `chronos-1.1` as `1.0.9` is not Windows-compatible.
   (by [@vrom911](https://github.com/vrom911))
-* [#156](https://github.com/kowainik/co-log/issues/156):
+* [#156](https://github.com/co-log/co-log/issues/156):
   Improve documentation for the `Colog.Concurrent` module.
   (by [@chshersh](https://github.com/chshersh))
-* [#146](https://github.com/kowainik/co-log/issues/146):
+* [#146](https://github.com/co-log/co-log/issues/146):
   Allow `ansi-terminal-0.10`.
   (by [@mpilgrem](https://github.com/mpilgrem))
-* [#124](https://github.com/kowainik/co-log/issues/124):
+* [#124](https://github.com/co-log/co-log/issues/124):
   Implement executable playground for concurrent logging.
   (by [@chshersh](https://github.com/chshersh))
 
 ## 0.3.0.0 — May 5, 2019
 
-* [#77](https://github.com/kowainik/co-log/issues/77):
+* [#77](https://github.com/co-log/co-log/issues/77):
   **Important:** Use `chronos` time formatter. This is a breaking change because
   default field map in `RichMessage` now contains different type representing
   time. If you use your custom formatter for time, you should change it.
   Othwerwise no observable differences in the library API usage will be noticed.
-* [#103](https://github.com/kowainik/co-log/issues/103):
+* [#103](https://github.com/co-log/co-log/issues/103):
   **Breaking change:** make `Message` data type polymorhic over the type of severity.
 
   **Migration guide:** this change is done in backwards-compatible way. If you
@@ -62,30 +142,30 @@
   messageText     -> msgText
   ```
 * Export more formatting functions to make implementation of custom formatters easier.
-* [#96](https://github.com/kowainik/co-log/issues/96):
+* [#96](https://github.com/co-log/co-log/issues/96):
   Add `simpleMessageAction` and `richMessageAction` to work with `Message`s.
 * Use `co-log-core` of version `0.2.0.0`.
 
 ## 0.2.0 — Nov 15, 2018
 
-* [#45](https://github.com/kowainik/co-log/issues/45):
+* [#45](https://github.com/co-log/co-log/issues/45):
   Introduce approach for concurrent log writing.
-* [#46](https://github.com/kowainik/co-log/issues/46):
+* [#46](https://github.com/co-log/co-log/issues/46):
   Moves `logStringStdout`, `logStringStderr`, `logStringHandle`,
   `withLogStringFile` from `Colog.Actions` to `Colog.Core.IO`
-* [#77](https://github.com/kowainik/co-log/issues/77):
+* [#77](https://github.com/co-log/co-log/issues/77):
   Remove `relude` from dependencies.
   Add HLint check to Travis CI.
-* [#64](https://github.com/kowainik/co-log/issues/64):
+* [#64](https://github.com/co-log/co-log/issues/64):
   Introduce basic benchmarks.
-* [#20](https://github.com/kowainik/co-log/issues/20):
+* [#20](https://github.com/co-log/co-log/issues/20):
   Add experimental support for logger rotation (see `Colog.Rotation` module).
-* [#39](https://github.com/kowainik/co-log/issues/39):
+* [#39](https://github.com/co-log/co-log/issues/39):
   Support GHC-8.2.2 and GHC-8.6.2.
 
 ## 0.1.0
 
-* [#37](https://github.com/kowainik/co-log/issues/37):
+* [#37](https://github.com/co-log/co-log/issues/37):
   Add bounds to all dependencies. Move `Prelude` to the
   `other-modules` section.
 
@@ -94,4 +174,4 @@
 * Initially created.
 
 [1]: https://pvp.haskell.org
-[2]: https://github.com/kowainik/co-log/releases
+[2]: https://github.com/co-log/co-log/releases
diff --git a/README.lhs b/README.lhs
--- a/README.lhs
+++ b/README.lhs
@@ -1,23 +1,55 @@
 # co-log
 
-[![Hackage](https://img.shields.io/hackage/v/co-log.svg)](https://hackage.haskell.org/package/co-log)
-[![Stackage LTS](http://stackage.org/package/co-log/badge/lts)](http://stackage.org/lts/package/co-log)
-[![Stackage Nightly](http://stackage.org/package/co-log/badge/nightly)](http://stackage.org/nightly/package/co-log)
-[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](https://github.com/kowainik/co-log/blob/master/LICENSE)
-[![Build status](https://secure.travis-ci.org/kowainik/co-log.svg)](https://travis-ci.org/kowainik/co-log)
+<img align="left" width="180" height="180" src="https://user-images.githubusercontent.com/8126674/80955687-92f21a80-8df7-11ea-90d3-422dafdc8391.png">
 
+[![GitHub CI](https://github.com/co-log/co-log/workflows/CI/badge.svg)](https://github.com/co-log/co-log/actions)
+[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](https://github.com/co-log/co-log/blob/main/LICENSE)
 
-Logging library based on [`co-log-core`](../co-log-core) package. Provides
-ready-to-go implementation of logging. This README contains _How to_ tutorial on
-using this library. This tutorial explains step by step how to integrate
-`co-log` into small basic project, specifically how to replace `putStrLn` used
-for logging with library provided logging.
+`co-log` is a composable and configurable logging framework. It
+combines all the benefits of Haskell idioms to provide a reasonable
+and convenient interface. Although the library design uses some advanced
+concepts in its core, we are striving to provide beginner-friendly API. The
+library also provides the complete documentation with a lot of beginner-friendly
+examples, explanations and tutorials to guide users. The combination of a
+pragmatic approach to logging and fundamental Haskell abstractions allows us to
+create a highly composable and configurable logging framework.
 
+---
+
+If you're interested in how different Haskell typeclasses are used to
+implement core functions of `co-log`, you can read the following blog
+post which goes into detail about the internal implementation specifics:
+
+- [co-log: Composable Contravariant Combinatorial Comonadic Configurable Convenient Logging](https://kowainik.github.io/posts/2018-09-25-co-log)
+
+## Co-Log Family
+
+Co-Log is a family of repositories for a composable and configurable logging
+framework `co-log`.
+
+Here is the list of currently available repositories and libraries that you can
+check out:
+
+|                                                                    |                                                                                                                                                        |                                    |
+| :----------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------- |
+| [`co-log-core`](https://github.com/co-log/co-log-core)             | lightweight package with basic data types and general idea which depends only on `base`                                                                | [![Hackage][hk-img-core]][hk-core] |
+| [`co-log`](https://github.com/co-log/co-log)                       | taggless final implementation of logging library based on `co-log-core`                                                                                | [![Hackage][hk-img]][hk]           |
+| [`co-log-polysemy`](https://github.com/co-log/co-log-polysemy)     | implementation of logging library based on `co-log-core` and the [`polysemy`](http://hackage.haskell.org/package/polysemy) extensible effects library. | [![Hackage][hk-img-ps]][hk-ps]     |
+| [`co-log-benchmarks`](https://github.com/co-log/co-log-benchmarks) | benchmarks of the `co-log` library                                                                                                                     | -                                  |
+
+## `co-log` library
+
+Logging library based on [`co-log-core`](https://github.com/co-log/co-log-core)
+package. Provides ready-to-go implementation of logging. This README contains
+_How to_ tutorial on using this library. This tutorial explains step by step how
+to integrate `co-log` into small basic project, specifically how to replace
+`putStrLn` used for logging with library provided logging.
+
 All code below can be compiled and run with the following commands:
 
 ```shell
-$ cabal new-build co-log
-$ cabal new-exec readme
+$ cabal build
+$ cabal exec readme
 ```
 
 ## Preamble: imports and language extensions
@@ -29,14 +61,13 @@
 {-# LANGUAGE FlexibleContexts  #-}
 {-# LANGUAGE OverloadedStrings #-}
 
+import Control.Monad.IO.Class (MonadIO, liftIO)
+
 import Colog (Message, WithLog, cmap, fmtMessage, logDebug, logInfo, logTextStdout, logWarning,
               usingLoggerT)
-import Control.Monad.IO.Class (MonadIO, liftIO)
 
-import Data.Semigroup ((<>))
 import qualified Data.Text as Text
 import qualified Data.Text.IO as TextIO
-
 ```
 
 ## Simple IO function example
@@ -103,3 +134,16 @@
 And here is how output looks like:
 
 ![screenshot from 2018-09-17 20-52-01](https://user-images.githubusercontent.com/4276606/45623973-8bafb900-babb-11e8-9e20-4369a5a8e5ff.png)
+
+## More Tutorials
+
+To provide a more user-friendly introduction to the library, we've
+created the tutorial series which introduces the main concepts behind `co-log`
+smoothly, please [check more details here](./tutorials/README.md).
+
+[hk-img]: https://img.shields.io/hackage/v/co-log.svg?logo=haskell
+[hk-img-ps]: https://img.shields.io/hackage/v/co-log-polysemy.svg?logo=haskell
+[hk-img-core]: https://img.shields.io/hackage/v/co-log-core.svg?logo=haskell
+[hk]: https://hackage.haskell.org/package/co-log
+[hk-ps]: https://hackage.haskell.org/package/co-log-polysemy
+[hk-core]: https://hackage.haskell.org/package/co-log-core
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,23 +1,55 @@
 # co-log
 
-[![Hackage](https://img.shields.io/hackage/v/co-log.svg)](https://hackage.haskell.org/package/co-log)
-[![Stackage LTS](http://stackage.org/package/co-log/badge/lts)](http://stackage.org/lts/package/co-log)
-[![Stackage Nightly](http://stackage.org/package/co-log/badge/nightly)](http://stackage.org/nightly/package/co-log)
-[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](https://github.com/kowainik/co-log/blob/master/LICENSE)
-[![Build status](https://secure.travis-ci.org/kowainik/co-log.svg)](https://travis-ci.org/kowainik/co-log)
+<img align="left" width="180" height="180" src="https://user-images.githubusercontent.com/8126674/80955687-92f21a80-8df7-11ea-90d3-422dafdc8391.png">
 
+[![GitHub CI](https://github.com/co-log/co-log/workflows/CI/badge.svg)](https://github.com/co-log/co-log/actions)
+[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](https://github.com/co-log/co-log/blob/main/LICENSE)
 
-Logging library based on [`co-log-core`](../co-log-core) package. Provides
-ready-to-go implementation of logging. This README contains _How to_ tutorial on
-using this library. This tutorial explains step by step how to integrate
-`co-log` into small basic project, specifically how to replace `putStrLn` used
-for logging with library provided logging.
+`co-log` is a composable and configurable logging framework. It
+combines all the benefits of Haskell idioms to provide a reasonable
+and convenient interface. Although the library design uses some advanced
+concepts in its core, we are striving to provide beginner-friendly API. The
+library also provides the complete documentation with a lot of beginner-friendly
+examples, explanations and tutorials to guide users. The combination of a
+pragmatic approach to logging and fundamental Haskell abstractions allows us to
+create a highly composable and configurable logging framework.
 
+---
+
+If you're interested in how different Haskell typeclasses are used to
+implement core functions of `co-log`, you can read the following blog
+post which goes into detail about the internal implementation specifics:
+
+- [co-log: Composable Contravariant Combinatorial Comonadic Configurable Convenient Logging](https://kowainik.github.io/posts/2018-09-25-co-log)
+
+## Co-Log Family
+
+Co-Log is a family of repositories for a composable and configurable logging
+framework `co-log`.
+
+Here is the list of currently available repositories and libraries that you can
+check out:
+
+|                                                                    |                                                                                                                                                        |                                    |
+| :----------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------- |
+| [`co-log-core`](https://github.com/co-log/co-log-core)             | lightweight package with basic data types and general idea which depends only on `base`                                                                | [![Hackage][hk-img-core]][hk-core] |
+| [`co-log`](https://github.com/co-log/co-log)                       | taggless final implementation of logging library based on `co-log-core`                                                                                | [![Hackage][hk-img]][hk]           |
+| [`co-log-polysemy`](https://github.com/co-log/co-log-polysemy)     | implementation of logging library based on `co-log-core` and the [`polysemy`](http://hackage.haskell.org/package/polysemy) extensible effects library. | [![Hackage][hk-img-ps]][hk-ps]     |
+| [`co-log-benchmarks`](https://github.com/co-log/co-log-benchmarks) | benchmarks of the `co-log` library                                                                                                                     | -                                  |
+
+## `co-log` library
+
+Logging library based on [`co-log-core`](https://github.com/co-log/co-log-core)
+package. Provides ready-to-go implementation of logging. This README contains
+_How to_ tutorial on using this library. This tutorial explains step by step how
+to integrate `co-log` into small basic project, specifically how to replace
+`putStrLn` used for logging with library provided logging.
+
 All code below can be compiled and run with the following commands:
 
 ```shell
-$ cabal new-build co-log
-$ cabal new-exec readme
+$ cabal build
+$ cabal exec readme
 ```
 
 ## Preamble: imports and language extensions
@@ -29,14 +61,13 @@
 {-# LANGUAGE FlexibleContexts  #-}
 {-# LANGUAGE OverloadedStrings #-}
 
+import Control.Monad.IO.Class (MonadIO, liftIO)
+
 import Colog (Message, WithLog, cmap, fmtMessage, logDebug, logInfo, logTextStdout, logWarning,
               usingLoggerT)
-import Control.Monad.IO.Class (MonadIO, liftIO)
 
-import Data.Semigroup ((<>))
 import qualified Data.Text as Text
 import qualified Data.Text.IO as TextIO
-
 ```
 
 ## Simple IO function example
@@ -103,3 +134,16 @@
 And here is how output looks like:
 
 ![screenshot from 2018-09-17 20-52-01](https://user-images.githubusercontent.com/4276606/45623973-8bafb900-babb-11e8-9e20-4369a5a8e5ff.png)
+
+## More Tutorials
+
+To provide a more user-friendly introduction to the library, we've
+created the tutorial series which introduces the main concepts behind `co-log`
+smoothly, please [check more details here](./tutorials/README.md).
+
+[hk-img]: https://img.shields.io/hackage/v/co-log.svg?logo=haskell
+[hk-img-ps]: https://img.shields.io/hackage/v/co-log-polysemy.svg?logo=haskell
+[hk-img-core]: https://img.shields.io/hackage/v/co-log-core.svg?logo=haskell
+[hk]: https://hackage.haskell.org/package/co-log
+[hk-ps]: https://hackage.haskell.org/package/co-log-polysemy
+[hk-core]: https://hackage.haskell.org/package/co-log-core
diff --git a/co-log.cabal b/co-log.cabal
--- a/co-log.cabal
+++ b/co-log.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.4
 name:                co-log
-version:             0.4.0.1
+version:             0.7.0.0
 synopsis:            Composable Contravariant Comonadic Logging Library
 description:
     The default implementation of logging based on [co-log-core](http://hackage.haskell.org/package/co-log-core).
@@ -9,30 +9,37 @@
     .
     * [co-log: Composable Contravariant Combinatorial Comonadic Configurable Convenient Logging](https://kowainik.github.io/posts/2018-09-25-co-log)
 
-homepage:            https://github.com/kowainik/co-log
-bug-reports:         https://github.com/kowainik/co-log/issues
+homepage:            https://github.com/co-log/co-log
+bug-reports:         https://github.com/co-log/co-log/issues
 license:             MPL-2.0
 license-file:        LICENSE
 author:              Dmitrii Kovanikov
 maintainer:          Kowainik <xrom.xkov@gmail.com>
-copyright:           2018-2020 Kowainik
+copyright:           2018-2022 Kowainik, 2023-2025 Co-Log
 category:            Logging, Contravariant, Comonad
 build-type:          Simple
 stability:           provisional
 extra-doc-files:     CHANGELOG.md
                      README.md
-tested-with:         GHC == 8.2.2
-                     GHC == 8.4.4
-                     GHC == 8.6.5
-                     GHC == 8.8.3
-                     GHC == 8.10.1
+tested-with:         GHC == 8.10.7
+                     GHC == 9.0.2
+                     GHC == 9.2.8
+                     GHC == 9.4.8
+                     GHC == 9.6.6
+                     GHC == 9.8.4
+                     GHC == 9.10.1
+                     GHC == 9.12.1
 
+flag tutorial
+  description: Controls if tutorials get build (mainly to avoid building them on hackage).
+  default: False
+
 source-repository head
   type:                git
-  location:            https://github.com/kowainik/co-log.git
+  location:            https://github.com/co-log/co-log.git
 
 common common-options
-  build-depends:       base >= 4.10.1.0 && < 4.15
+  build-depends:       base >= 4.14 && < 4.22
 
   ghc-options:         -Wall
                        -Wcompat
@@ -49,6 +56,11 @@
     ghc-options:       -Wmissing-deriving-strategies
   if impl(ghc >= 8.10)
     ghc-options:       -Wunused-packages
+  if impl(ghc >= 9.0)
+    ghc-options:       -Winvalid-haddock
+  if impl(ghc >= 9.2)
+    ghc-options:       -Wredundant-bang-patterns
+                       -Woperator-whitespace
 
   default-language:    Haskell2010
   default-extensions:  ConstraintKinds
@@ -66,40 +78,49 @@
 
 common tutorial-options
   import:              common-options
-  if os(windows)
+  if os(windows) || !flag(tutorial)
     buildable:         False
   build-depends:       co-log-core
+                       , text
 
-  build-tool-depends:  markdown-unlit:markdown-unlit
+  build-tool-depends:  markdown-unlit:markdown-unlit >= 0.5.0 && < 0.7
   ghc-options:         -pgmL markdown-unlit
 
+common tutorial-depends
+  import: tutorial-options
+  build-depends:     co-log
+                     , mtl
+
+
 library
   import:              common-options
   hs-source-dirs:      src
   exposed-modules:     Colog
-                           Colog.Actions
-                           Colog.Concurrent
-                               Colog.Concurrent.Internal
-                           Colog.Contra
-                           Colog.Message
-                           Colog.Monad
-                           Colog.Pure
-                           Colog.Rotation
+                       Colog.Actions
+                       Colog.Contra
+                       Colog.Message
+                       Colog.Monad
+                       Colog.Pure
+                       Colog.Rotation
 
-  build-depends:       ansi-terminal ^>= 0.10
-                     , bytestring ^>= 0.10.8
-                     , co-log-core ^>= 0.2.1.0
-                     , containers >= 0.5.7 && < 0.7
+  build-depends:       ansi-terminal >= 1.0 && < 1.2
+                     , bytestring >= 0.10.8 && < 0.13
+                     , co-log-core ^>= 0.3
+                     , containers >= 0.5.7 && < 0.9
                      , contravariant ^>= 1.5
                      , directory ^>= 1.3.0
-                     , filepath ^>= 1.4.1
-                     , mtl ^>= 2.2.2
-                     , stm >= 2.4 && < 2.6
-                     , text ^>= 1.2.3
-                     , chronos ^>= 1.1
-                     , transformers ^>= 0.5
-                     , typerep-map ^>= 0.3.2
-                     , vector ^>= 0.12.0.3
+                     , exceptions >= 0.8.3 && < 0.11
+                     , filepath >= 1.4.1 && < 1.6
+                     , mtl >= 2.2.2 && < 2.4
+                     , text >= 1.2.3 && < 2.2
+                     , time >= 1.8 && < 1.15
+                     , transformers >= 0.5 && < 0.7
+                     , dependent-sum >= 0.7 && < 0.8
+                     , dependent-map >= 0.4 && < 0.5
+                     , unliftio-core ^>= 0.2
+                     , vector >= 0.12.0.3 && < 0.14
+  if impl(ghc < 9.4.5)
+    build-depends:     run-st <= 0.1.3.0
 
 executable play-colog
   import:              common-options
@@ -108,7 +129,7 @@
 
   build-depends:       co-log
                      , mtl
-                     , typerep-map
+                     , dependent-map
 
   ghc-options:         -threaded
                        -rtsopts
@@ -130,21 +151,42 @@
   build-depends:       co-log
                      , text
 
-executable tutorial-intro
-  import:              tutorial-options
-  main-is:             tutorials/1-intro/Intro.lhs
-
-executable tutorial-custom
-  import:              tutorial-options
-  main-is:             tutorials/2-custom/Custom.lhs
-  build-depends:       co-log
-                     , mtl
-
 test-suite test-co-log
   import:              common-options
   build-depends:       co-log
                      , co-log-core
-                     , hedgehog ^>= 1.0
+                     , hedgehog >= 1.0 && < 1.6
   hs-source-dirs:      test
   main-is:             Property.hs
   type:                exitcode-stdio-1.0
+
+test-suite co-log-doctest
+  import:              common-options
+  -- Disable `doctest` on windows since it couldn't handle qualified imports reliable (which leads to errors like "Not in scope: `C.timeToOffsetDatetime'").
+  if os(windows)
+    buildable:         False
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Doctest.hs
+
+  build-depends:       doctest >= 0.16.0 && < 0.25
+                     , Glob ^>= 0.10.0
+  ghc-options:         -threaded
+
+
+executable tutorial-intro
+  import:              tutorial-options
+  main-is:             tutorials/1-intro/Intro.lhs
+
+executable tutorial-loggert-simple
+  import:              tutorial-depends
+  main-is:             tutorials/2-loggert/loggert.lhs
+  
+executable tutorial-loggert
+  import:              tutorial-depends
+  main-is:             tutorials/3-loggert-with-message/loggert.lhs
+
+executable tutorial-custom
+  import:              tutorial-depends
+  main-is:             tutorials/custom/Custom.lhs
+
diff --git a/src/Colog.hs b/src/Colog.hs
--- a/src/Colog.hs
+++ b/src/Colog.hs
@@ -1,7 +1,6 @@
 {- |
-Copyright:  (c) 2018-2020 Kowainik
+Copyright:  (c) 2018-2022 Kowainik, 2023-2025 Co-Log
 SPDX-License-Identifier: MPL-2.0
-Maintainer: Kowainik <xrom.xkov@gmail.com>
 
 This package contains @mtl@ implementation of composable, contravariant and
 comonadic logging based on @co-log-core@.
@@ -9,7 +8,6 @@
 
 module Colog
        ( module Colog.Actions
-       , module Colog.Concurrent
        , module Colog.Core
        , module Colog.Message
        , module Colog.Monad
@@ -18,7 +16,6 @@
        ) where
 
 import Colog.Actions
-import Colog.Concurrent
 import Colog.Contra ()
 import Colog.Core
 import Colog.Message
diff --git a/src/Colog/Actions.hs b/src/Colog/Actions.hs
--- a/src/Colog/Actions.hs
+++ b/src/Colog/Actions.hs
@@ -1,7 +1,6 @@
 {- |
-Copyright:  (c) 2018-2020 Kowainik
+Copyright:  (c) 2018-2022 Kowainik, 2023-2025 Co-Log
 SPDX-License-Identifier: MPL-2.0
-Maintainer: Kowainik <xrom.xkov@gmail.com>
 
 Logging actions for various text types.
 -}
@@ -27,9 +26,10 @@
 
 import Control.Monad.IO.Class (MonadIO (..))
 import Data.Text.Encoding (encodeUtf8)
-import System.IO (Handle, IOMode (AppendMode), stderr, withFile)
+import System.IO (Handle, IOMode (AppendMode), stderr, stdout, withFile)
 
 import Colog.Core.Action (LogAction (..), cmapM, (>$<))
+import Colog.Core.IO (logFlush)
 import Colog.Message (Message, defaultFieldMap, fmtMessage, fmtRichMessageDefault,
                       upgradeMessageAction)
 
@@ -42,19 +42,28 @@
 -- ByteString
 ----------------------------------------------------------------------------
 
-{- | Action that prints 'BS.ByteString' to stdout. -}
+{- | Action that prints 'BS.ByteString' to stdout.
+This action does not flush the output buffer.
+If buffering mode is block buffering, the effect of this action can be delayed.
+-}
 logByteStringStdout :: MonadIO m => LogAction m BS.ByteString
 logByteStringStdout = LogAction $ liftIO . BS8.putStrLn
 {-# INLINE logByteStringStdout #-}
 {-# SPECIALIZE logByteStringStdout :: LogAction IO BS.ByteString #-}
 
-{- | Action that prints 'BS.ByteString' to stderr. -}
+{- | Action that prints 'BS.ByteString' to stderr.
+This action does not flush the output buffer.
+If buffering mode is block buffering, the effect of this action can be delayed.
+-}
 logByteStringStderr :: MonadIO m => LogAction m BS.ByteString
 logByteStringStderr = logByteStringHandle stderr
 {-# INLINE logByteStringStderr #-}
 {-# SPECIALIZE logByteStringStderr :: LogAction IO BS.ByteString #-}
 
-{- | Action that prints 'BS.ByteString' to 'Handle'. -}
+{- | Action that prints 'BS.ByteString' to 'Handle'.
+This action does not flush the output buffer.
+If buffering mode is block buffering, the effect of this action can be delayed.
+-}
 logByteStringHandle :: MonadIO m => Handle -> LogAction m BS.ByteString
 logByteStringHandle handle = LogAction $ liftIO . BS8.hPutStrLn handle
 {-# INLINE logByteStringHandle #-}
@@ -64,7 +73,8 @@
 'Colog.Core.Action.withLogStringFile' for details.
 -}
 withLogByteStringFile :: MonadIO m => FilePath -> (LogAction m BS.ByteString -> IO r) -> IO r
-withLogByteStringFile path action = withFile path AppendMode $ action . logByteStringHandle
+withLogByteStringFile path action = withFile path AppendMode $ \handle ->
+  action (logByteStringHandle handle <> logFlush handle)
 {-# INLINE withLogByteStringFile #-}
 {-# SPECIALIZE withLogByteStringFile :: FilePath -> (LogAction IO BS.ByteString -> IO r) -> IO r #-}
 
@@ -72,21 +82,30 @@
 -- Text
 ----------------------------------------------------------------------------
 
-{- | Action that prints 'T.Text' to stdout. -}
+{- | Action that prints 'T.Text' to stdout.
+This action does not flush the output buffer.
+If buffering mode is block buffering, the effect of this action can be delayed.
+-}
 logTextStdout :: MonadIO m => LogAction m T.Text
-logTextStdout = LogAction $ liftIO . TIO.putStrLn
+logTextStdout = logTextHandle stdout
 {-# INLINE logTextStdout #-}
 {-# SPECIALIZE logTextStdout :: LogAction IO T.Text #-}
 
-{- | Action that prints 'T.Text' to stderr. -}
+{- | Action that prints 'T.Text' to stderr.
+This action does not flush the output buffer.
+If buffering mode is block buffering, the effect of this action can be delayed.
+-}
 logTextStderr :: MonadIO m => LogAction m T.Text
 logTextStderr = logTextHandle stderr
 {-# INLINE logTextStderr #-}
 {-# SPECIALIZE logTextStderr :: LogAction IO T.Text #-}
 
-{- | Action that prints 'T.Text' to 'Handle'. -}
+{- | Action that prints 'T.Text' to 'Handle'.
+This action does not flush the output buffer.
+If buffering mode is block buffering, the effect of this action can be delayed.
+-}
 logTextHandle :: MonadIO m => Handle -> LogAction m T.Text
-logTextHandle handle = LogAction $ liftIO . TIO.hPutStrLn handle
+logTextHandle handle = LogAction $ \m -> liftIO . TIO.hPutStr handle $ m <> "\n"
 {-# INLINE logTextHandle #-}
 {-# SPECIALIZE logTextHandle :: Handle -> LogAction IO T.Text #-}
 
@@ -94,7 +113,8 @@
 'Colog.Core.Action.withLogStringFile' for details.
 -}
 withLogTextFile :: MonadIO m => FilePath -> (LogAction m T.Text -> IO r) -> IO r
-withLogTextFile path action = withFile path AppendMode $ action . logTextHandle
+withLogTextFile path action = withFile path AppendMode $ \handle ->
+  action (logTextHandle handle <> logFlush handle)
 {-# INLINE withLogTextFile #-}
 {-# SPECIALIZE withLogTextFile :: FilePath -> (LogAction IO T.Text -> IO r) -> IO r #-}
 
diff --git a/src/Colog/Concurrent.hs b/src/Colog/Concurrent.hs
deleted file mode 100644
--- a/src/Colog/Concurrent.hs
+++ /dev/null
@@ -1,343 +0,0 @@
-{- |
-Copyright:  (c) 2018-2020 Kowainik
-SPDX-License-Identifier: MPL-2.0
-Maintainer: Kowainik <xrom.xkov@gmail.com>
-
-__NOTE:__ Many thanks to Alexander Vershilov for the implementation.
-
-For the speed reasons you may want to dump logs asynchronously.
-This is especially useful when application threads are CPU
-bound while logs emitting is I/O bound. This approach
-allows to mitigate bottlenecks from the I/O.
-
-When writing an application user should be aware of the tradeoffs
-that concurrent log system can provide, in this module we explain
-potential tradeoffs and describe if certain building blocks are
-affected or not.
-
-  1. Unbounded memory usage - if there is no backpressure mechanism
-  the user threads, they may generate more logs that can be
-  written in the same amount of time. In those cases messages will
-  be accumulated in memory. That will lead to extended GC times and
-  application may be killed by the operating systems mechanisms.
-
-  2. Persistence requirement - sometimes application may want to
-  ensure that logs were written before it can continue. This is not
-  a case with concurrent log systems in general, and some logs may
-  be lost when application exits before dumping all logs.
-
-  3. Non-precise logging - sometimes it may happen that there can be
-  logs reordering (in case if thread was moved to another capability).
-
-In case if your application is a subject of those problems you may
-consider not using concurrent logging system in other cases concurrent
-logger may be a good default for you.
--}
-
-module Colog.Concurrent
-       ( -- $general
-         -- * Simple API.
-         -- $simple-api
-         withBackgroundLogger
-       , defCapacity
-         -- * Extended API
-         -- $extended-api
-         -- ** Background worker
-         -- $background-worker
-       , BackgroundWorker
-       , backgroundWorkerWrite
-       , killBackgroundLogger
-         -- ** Background logger
-       , forkBackgroundLogger
-       , convertToLogAction
-         -- ** Worker thread
-         -- $worker-thread
-       , mkBackgroundThread
-       , runInBackgroundThread
-         -- *** Usage example
-         -- $worker-thread-usage
-       ) where
-
-import Control.Applicative (many)
-import Control.Concurrent (forkFinally, killThread)
-import Control.Concurrent.STM (atomically, check, newTVarIO, readTVar, writeTVar)
-import Control.Concurrent.STM.TBQueue (newTBQueueIO, readTBQueue, writeTBQueue)
-import Control.Exception (bracket, finally)
-import Control.Monad (forever, join)
-import Control.Monad.IO.Class (MonadIO (..))
-import Data.Foldable (for_)
-
-import Colog.Concurrent.Internal (BackgroundWorker (..), Capacity (..))
-import Colog.Core.Action (LogAction (..))
-
-
-{- $general
-
-Concurrent logger consists of the basic parts (see schema below).
-
-  1. Logger in application thread. This logger is evaluated in the
-  application thread and has an access to all the context available
-  in that thread and monad, this logger can work in any @m@.
-
-  2. Communication channel with backpressure support. In addition to
-  the channel we have a converter that puts the user message to the
-  communication channel. This converter works in the user thread.
-  Such a logger usually works in 'IO' but it's possible to make it
-  work in 'Control.Concurrent.STM.STM' as well. At this point library provides only 'IO'
-  version, but it can be lifted to any 'MonadIO' by the user.
-
-  3. Logger thread. This is the thread that performs actual write to
-  the sinks. Loggers there do not have access to the users thread
-  state, unless that state was passed in the message.
-
-
-@
-
- +-------------------------+                  +--------------------------------+
- |                         |                  | Logger        |   Sink-1       |
- |   Application Thread    |                  | Thread    +--->                |
- |   -----------------     |  +-----------+   |           |   +----------------+
- |                         |  |           |   +---------+ |   +----------------+
- |           +-------------+  |  channel  |   | Shared  +----->   Sink-2       |
- |           | application||  |          +----> logger  | |   |                |
- |           | logger    +----->          |   +---------+ |   +----------------+
- |           +-------------+  |           |   |           |   +----------------+
- |                         |  +-----------+   |           +--->   Sink3        |
- |                         |                  |               |                |
- |                         |                  |               +----------------+
- |                         |                  |                                |
- +-------------------------+                  +--------------------------------+
-@
-
-So usually user should write the logging system in the way that all 'LogAction'
-that populate and filter information should live in the application logger.
-All loggers that do serialization and formatting should live in shared logger.
-
-
-If more concurrency is needed it's possible to build multilayer systems:
-
-
-@
-  +-------------+                         +-------+
-  | application |---+                 +---| sink-1|
-  +-------------+   |   +---------+   |   +-------+
-                    +---| logger  |---+
-                        +---------+   |   +-------+
-                                      +---| sink-2|
-                                          +-------+
-@
-
-In this approach application will be concurrently write logs to the logger, then
-logger will be concurrently writing to all sinks.
--}
-
-{- $simple-api
-
-Simple API provides a handy easy to use API that can be used directly
-in application without dealing with internals. Based on users feedback
-internal implementation of the simple API may change, especially in early
-versions of the library. But the guarantee that we give is that no matter
-what implementation is it will be kept with reasonable defaults and will
-be applicable to a generic application.
--}
-
-{- | An exception safe way to create background logger.  This method will fork
-a thread that will run 'shared worker', see schema above.
-
-@Capacity@ - provides a backpressure mechanism and tells how many messages
-in flight are allowed. In most cases 'defCapacity' will work well.
-See 'forkBackgroundLogger' for more details.
-
-@LogAction@ - provides a logger action, this action does not have access to the
-application state or thread info, so you should only pass methods that serialize
-and dump data there.
-
-@
-main :: IO ()
-main =
-  'withBackgroundLogger'
-     'defCapacity'
-     'Colog.Actions.logByteStringStdout'
-     (\log -> 'Colog.Monad.usingLoggerT' log $ __do__
-        'Colog.Monad.logMsg' \@ByteString "Starting application..."
-        'Colog.Monad.logMsg' \@ByteString "Finishing application..."
-     )
-@
--}
-withBackgroundLogger
-    :: MonadIO m
-    => Capacity  -- ^ Capacity of messages to handle; bounded channel size
-    -> LogAction IO msg  -- ^ Action that will be used in a forked thread
-    -> (LogAction m msg -> IO a)  -- ^ Continuation action
-    -> IO a
-withBackgroundLogger cap logger action =
-   bracket (forkBackgroundLogger cap logger)
-           killBackgroundLogger
-           (action . convertToLogAction)
-
--- | Default capacity size, (4096)
-defCapacity :: Capacity
-defCapacity = Capacity 4096
-
-
-{- $extended-api
-
-Extended API explains how asynchronous logging is working and provides basic
-building blocks for writing your own combinators. This is the part of the public
-API and will not change without prior notice.
--}
-
-{- $background-worker
-The main abstraction for the concurrent worker is 'BackgroundWorker'. This
-is a wrapper of the thread, that has communication channel to talk to, and threadId.
-
-Background worker may provide a backpressure mechanism, but does not provide
-notification of completeness unless it's included in the message itself.
--}
-
-{- | Stop background logger thread.
-
-The thread is blocked until background thread will finish processing
-all messages that were written in the channel.
--}
-killBackgroundLogger :: BackgroundWorker msg -> IO ()
-killBackgroundLogger bl = do
-  killThread (backgroundWorkerThreadId bl)
-  atomically $ readTVar (backgroundWorkerIsAlive bl) >>= check . not
-
-{- $background-logger
-
-Background logger is specialized version of the 'BackgroundWorker' process.
-Instead of running any job it will accept @msg@ type
-instead and process it with a single logger defined at creation time.
--}
-
-{- | Creates background logger with given @Capacity@,
-takes a 'LogAction' that should describe how to write
-logs.
-
-@capacity@ - parameter tells how many in flight messages are allowed,
-if that value is reached then user's thread that emits logs will be
-blocked until any message will be written. Usually if value should be
-chosen reasonably high and if this value is reached it means that
-the application environment experience severe problems.
-
-__N.B.__ The 'LogAction' will be run in the background
-thread so that logger should not add any thread specific
-context to the message.
-
-__N.B.__ On exit, even in case of exception thread will dump all values
-that are in the queue. But it will stop doing that in case if another
-exception will happen.
--}
-forkBackgroundLogger :: Capacity -> LogAction IO msg -> IO (BackgroundWorker msg)
-forkBackgroundLogger (Capacity cap) logAction = do
-  queue <- newTBQueueIO cap
-  isAlive <- newTVarIO True
-  tid <- forkFinally
-    (forever $ do
-      msg <- atomically $ readTBQueue queue
-      unLogAction logAction msg)
-    (\_ ->
-       (do msgs <- atomically $ many $ readTBQueue queue
-           for_ msgs $ unLogAction logAction)
-         `finally` atomically (writeTVar isAlive False))
-  pure $ BackgroundWorker tid (writeTBQueue queue) isAlive
-
-
-{- | Convert a given 'BackgroundWorker msg' into a 'LogAction msg'
-that will send log message to the background thread,
-without blocking the thread.
-
-If logger dies for any reason then thread that emits
-logs will receive 'BlockedIndefinitelyOnSTM' exception.
-
-You can extend result worker with all functionality available
-with co-log. This logger will have an access to the thread
-state.
--}
-convertToLogAction :: MonadIO m => BackgroundWorker msg -> LogAction m msg
-convertToLogAction logger = LogAction $ \msg ->
-  liftIO $ atomically $ backgroundWorkerWrite logger msg
-
-{- $worker-thread
-
-While generic background logger is enough for the most
-of the usecases, sometimes you may want even more.
-
-There are at least two cases where that may happen:
-
-  1. You need to modify logger, for example different
-  threads wants to write to different sources. Or you
-  want to change lgo mechanism in runtime.
-
-  2. You may want to implement some notification
-  machinery that allows you to guarantee that your
-  logs were written before processing further.
-
-In order to solve those problems worker thread abstraction
-was introduced. This is a worker that accepts any action
-and performs that.
--}
-
-{- | Create a background worker with a given capacity.
-If capacity is reached, then the thread that tries to
-write logs will be blocked.
-
-This method is more generic than 'forkBackgroundLogger' but
-it's less effective, as you have to pass entire closure to
-be run and that leads to extra memory usage and indirect calls
-happening.
-
-When closed it will dump all pending messages, unless
-another asynchronous exception will arrive, or synchronous
-exception will happen during the logging.
--}
-mkBackgroundThread :: Capacity -> IO (BackgroundWorker (IO ()))
-mkBackgroundThread (Capacity cap) = do
-  queue <- newTBQueueIO cap
-  isAlive <- newTVarIO True
-  tid <- forkFinally
-    (forever $ join $ atomically $ readTBQueue queue)
-    (\_ ->
-       (sequence_ =<< atomically (many $ readTBQueue queue))
-       `finally` atomically (writeTVar isAlive False))
-  pure $ BackgroundWorker tid (writeTBQueue queue) isAlive
-
-{- | Run logger action asynchronously in the worker thread.
-Logger is executed in the other thread entirely, so if
-logger takes any thread related context it will be
-read from the other thread.
--}
-runInBackgroundThread :: BackgroundWorker (IO ()) -> LogAction IO msg -> LogAction IO msg
-runInBackgroundThread bt logAction = LogAction $ \msg ->
-  atomically $ backgroundWorkerWrite bt $ unLogAction logAction msg
-
-{- $worker-thread-usage
-
-Consider following example. (Leaving resource control aside).
-
-@
-data M msg = M (MVar ()) msg
-
-notificationLogger :: MonadIO m => LoggerAction m msg -> LoggerAction m (M msg)
-notificationLogger logger = 'LogAction' $ \(M lock msg) ->
-   (unLogger logger msg) `finally` (putMVar lock ())
-
-example = __do__
-   worker <- 'mkBackgroundThread' 'defCapacity'
-   lock <- newEmptyMVar
-   -- Log message with default logger.
-   'unLogger'
-      ('runInBackgroundThread' worker
-      (notificationLogger $ 'Colog.Action.withLogByteStringFile' "\/var\/log\/myapp\/log")
-      (M lock "my message")
-   -- Log message with a different logger.
-   'unLogger'
-      ('runInBackgroundThread' worker
-      ('Colog.Action.withLogByteStringFile' "/var/log/myapp/log")
-      ("another message")
-   -- Block until first message is logged.
-   _ <- takeMVar lock
-@
--}
diff --git a/src/Colog/Concurrent/Internal.hs b/src/Colog/Concurrent/Internal.hs
deleted file mode 100644
--- a/src/Colog/Concurrent/Internal.hs
+++ /dev/null
@@ -1,42 +0,0 @@
-{-# LANGUAGE CPP #-}
-
-{- |
-Copyright:  (c) 2018-2020 Kowainik
-SPDX-License-Identifier: MPL-2.0
-Maintainer: Kowainik <xrom.xkov@gmail.com>
-
-__NOTE:__ Many thanks to Alexander Vershilov for the implementation.
-
-This is internal module, use it on your own risk. The implementation here may be
-changed without a version bump.
--}
-
-module Colog.Concurrent.Internal
-       ( BackgroundWorker (..)
-       , Capacity (..)
-       ) where
-
-import Control.Concurrent (ThreadId)
-import Control.Concurrent.STM (STM, TVar)
-import Numeric.Natural (Natural)
-
-
-{- | A wrapper type that carries capacity. The internal type may be
-differrent for the different GHC versions.
--}
-#if MIN_VERSION_stm(2,5,0)
-newtype Capacity = Capacity Natural
-#else
-newtype Capacity = Capacity Int
-#endif
-
-{- | Wrapper for the background thread that may receive messages to
-process.
--}
-data BackgroundWorker msg = BackgroundWorker
-    { backgroundWorkerThreadId :: !ThreadId
-      -- ^ Background 'ThreadId'.
-    , backgroundWorkerWrite    :: msg -> STM ()
-      -- ^ Method for communication with the thread.
-    , backgroundWorkerIsAlive  :: TVar Bool
-    }
diff --git a/src/Colog/Contra.hs b/src/Colog/Contra.hs
--- a/src/Colog/Contra.hs
+++ b/src/Colog/Contra.hs
@@ -3,9 +3,8 @@
 {-# LANGUAGE CPP #-}
 
 {- |
-Copyright:  (c) 2018-2020 Kowainik
+Copyright:  (c) 2018-2022 Kowainik, 2023-2025 Co-Log
 SPDX-License-Identifier: MPL-2.0
-Maintainer: Kowainik <xrom.xkov@gmail.com>
 
 This module contains 'LogAction' orphan instances of @contravariant@ classes.
 -}
diff --git a/src/Colog/Message.hs b/src/Colog/Message.hs
--- a/src/Colog/Message.hs
+++ b/src/Colog/Message.hs
@@ -5,13 +5,11 @@
 {-# LANGUAGE FlexibleInstances     #-}
 {-# LANGUAGE GADTs                 #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE OverloadedLabels      #-}
 {-# LANGUAGE TypeFamilies          #-}
 
 {- |
-Copyright:  (c) 2018-2020 Kowainik
+Copyright:  (c) 2018-2022 Kowainik, 2023-2025 Co-Log
 SPDX-License-Identifier: MPL-2.0
-Maintainer: Kowainik <xrom.xkov@gmail.com>
 
 This module contains logging messages data types along with the formatting and
 logging actions for them.
@@ -42,6 +40,9 @@
        , fmtMessage
        , showSeverity
        , showSourceLoc
+       , showTime
+       , showTimeOffset
+       , showThreadId
 
          -- * Externally extensible message type
          -- ** Field of the dependent map
@@ -62,33 +63,26 @@
        , upgradeMessageAction
        ) where
 
-import Prelude hiding (log)
+import Prelude hiding (lookup, log)
 
 import Control.Concurrent (ThreadId, myThreadId)
 import Control.Exception (Exception, displayException)
 import Control.Monad.IO.Class (MonadIO (..))
+import Data.Dependent.Map (DMap, fromList, lookup)
+import Data.Dependent.Sum (DSum ((:=>)))
 import Data.Kind (Type)
-import Data.Semigroup ((<>))
 import Data.Text (Text)
-import Data.Text.Lazy (toStrict)
-import Data.TypeRepMap (TypeRepMap)
-import GHC.Exts (IsList (..))
-import GHC.OverloadedLabels (IsLabel (..))
 import GHC.Stack (CallStack, SrcLoc (..), callStack, getCallStack, withFrozenCallStack)
-import GHC.TypeLits (KnownSymbol, Symbol)
+import GHC.TypeLits (Symbol)
 import System.Console.ANSI (Color (..), ColorIntensity (Vivid), ConsoleLayer (Foreground), SGR (..),
                             setSGRCode)
+import Type.Reflection (TypeRep, typeRep)
 
 import Colog.Core (LogAction, Severity (..), cmap)
 import Colog.Monad (WithLog, logMsg)
 
-import qualified Chronos as C
-import qualified Chronos.Locale.English as C
+import qualified Data.Time as C
 import qualified Data.Text as T
-import qualified Data.Text.Lazy.Builder as TB
-import qualified Data.Text.Lazy.Builder.Int as TB
-import qualified Data.TypeRepMap as TM
-import qualified Data.Vector as Vector
 
 ----------------------------------------------------------------------------
 -- Plain message
@@ -105,7 +99,7 @@
 data Msg sev = Msg
     { msgSeverity :: !sev
     , msgStack    :: !CallStack
-    , msgText     :: !Text
+    , msgText     ::  Text
     }
 
 {- | Message data type without 'Severity'. Use 'logText' to log
@@ -261,8 +255,8 @@
 types. The type family is open so you can add new instances.
 -}
 type family FieldType (fieldName :: Symbol) :: Type
-type instance FieldType "threadId"  = ThreadId
-type instance FieldType "posixTime" = C.Time
+type instance FieldType "threadId" = ThreadId
+type instance FieldType "utcTime"  = C.UTCTime
 
 {- | @newtype@ wrapper. Stores monadic ability to extract value of 'FieldType'.
 
@@ -295,15 +289,6 @@
 unMessageField (MessageField f) = f
 {-# INLINE unMessageField #-}
 
-instance (KnownSymbol fieldName, a ~ m (FieldType fieldName))
-      => IsLabel fieldName (a -> TM.WrapTypeable (MessageField m)) where
-#if MIN_VERSION_base(4,11,0)
-    fromLabel field = TM.WrapTypeable $ MessageField @fieldName field
-#else
-    fromLabel field = TM.WrapTypeable $ MessageField  @_ @fieldName field
-#endif
-    {-# INLINE fromLabel #-}
-
 -- | Helper function to deal with 'MessageField' when looking it up in the 'FieldMap'.
 extractField
     :: Applicative m
@@ -320,20 +305,20 @@
 {- | Depedent map from type level strings to the corresponding types. See
 'FieldType' for mapping between names and types.
 -}
-type FieldMap (m :: Type -> Type) = TypeRepMap (MessageField m)
+type FieldMap m = DMap TypeRep (MessageField m)
 
 {- | Default message map that contains actions to extract 'ThreadId' and
-'C.Time'. Basically, the following mapping:
+'C.UTCTime'. Basically, the following mapping:
 
 @
-"threadId"  -> 'myThreadId'
-"posixTime" -> 'C.now'
+"threadId" -> 'myThreadId'
+"utcTime"  -> 'C.getCurrentTime'
 @
 -}
 defaultFieldMap :: MonadIO m => FieldMap m
 defaultFieldMap = fromList
-    [ #threadId  (liftIO myThreadId)
-    , #posixTime (liftIO C.now)
+    [ typeRep @"threadId" :=> MessageField (liftIO myThreadId)
+    , typeRep @"utcTime"  :=> MessageField (liftIO C.getCurrentTime)
     ]
 
 {- | Contains additional data to 'Message' to display more verbose information.
@@ -342,7 +327,7 @@
 -}
 data RichMsg (m :: Type -> Type) (msg :: Type) = RichMsg
     { richMsgMsg :: !msg
-    , richMsgMap :: {-# UNPACK #-} !(FieldMap m)
+    , richMsgMap :: !(FieldMap m)
     } deriving stock (Functor)
 
 -- | Specialised version of 'RichMsg' that stores severity, callstack and text message.
@@ -366,7 +351,7 @@
 fmtRichMessageDefault :: MonadIO m => RichMessage m -> m Text
 fmtRichMessageDefault msg = fmtRichMessageCustomDefault msg formatRichMessage
   where
-    formatRichMessage :: Maybe ThreadId -> Maybe C.Time -> Message -> Text
+    formatRichMessage :: Maybe ThreadId -> Maybe C.UTCTime -> Message -> Text
     formatRichMessage (maybe "" showThreadId -> thread) (maybe "" showTime -> time) Msg{..} =
         showSeverity msgSeverity
      <> time
@@ -394,7 +379,7 @@
 fmtSimpleRichMessageDefault :: MonadIO m => RichMsg m SimpleMsg -> m Text
 fmtSimpleRichMessageDefault msg = fmtRichMessageCustomDefault msg formatRichMessage
   where
-    formatRichMessage :: Maybe ThreadId -> Maybe C.Time -> SimpleMsg -> Text
+    formatRichMessage :: Maybe ThreadId -> Maybe C.UTCTime -> SimpleMsg -> Text
     formatRichMessage (maybe "" showThreadId -> thread) (maybe "" showTime -> time) SimpleMsg{..} =
         time
      <> showSourceLoc simpleMsgStack
@@ -408,88 +393,38 @@
 fmtRichMessageCustomDefault
     :: MonadIO m
     => RichMsg m msg
-    -> (Maybe ThreadId -> Maybe C.Time -> msg -> Text)
+    -> (Maybe ThreadId -> Maybe C.UTCTime -> msg -> Text)
     -> m Text
 fmtRichMessageCustomDefault RichMsg{..} formatter = do
-    maybeThreadId  <- extractField $ TM.lookup @"threadId"  richMsgMap
-    maybePosixTime <- extractField $ TM.lookup @"posixTime" richMsgMap
-    pure $ formatter maybeThreadId maybePosixTime richMsgMsg
+    maybeThreadId <- extractField $ lookup (typeRep @"threadId")  richMsgMap
+    maybeUtcTime  <- extractField $ lookup (typeRep @"utcTime") richMsgMap
+    pure $ formatter maybeThreadId maybeUtcTime richMsgMsg
 
 {- | Shows time in the following format:
 
->>> showTime $ C.Time 1577656800
-[29 Dec 2019 22:00:00.000 +00:00]
+>>> showTime $ C.UTCTime (C.fromGregorian 2019 12 29) (C.secondsToDiffTime 3600 * 22)
+"[29 Dec 2019 22:00:00.000 +00:00] "
 -}
-showTime :: C.Time -> Text
-showTime t =
-    square
-    $ toStrict
-    $ TB.toLazyText
-    $ builderDmyHMSz (C.timeToDatetime t)
-
-----------------------------------------------------------------------------
--- Chronos extra
-----------------------------------------------------------------------------
+showTime :: C.UTCTime -> Text
+showTime = showTimeOffset . C.utcToZonedTime C.utc
 
-{- | Given a 'Datetime', constructs a 'Text' 'TB.Builder' corresponding to a
-Day\/Month\/Year,Hour\/Minute\/Second\/Offset encoding of the given 'Datetime'.
+{- | Shows time in the following format:
 
-Example: @29 Dec 2019 22:00:00.000 +00:00@
+>>> showTimeOffset $ C.utcToZonedTime (C.hoursToTimeZone (-2)) (C.UTCTime (C.fromGregorian 2019 12 29) (C.secondsToDiffTime 3600 * 22))
+"[29 Dec 2019 20:00:00.000 -02:00] "
 -}
-builderDmyHMSz :: C.Datetime -> TB.Builder
-builderDmyHMSz (C.Datetime date time) =
-       builderDmy date
-    <> spaceSep
-    <> C.builder_HMS (C.SubsecondPrecisionFixed 3) (Just ':') time
-    <> spaceSep
-    <> C.builderOffset C.OffsetFormatColonOn (C.Offset 0)
-  where
-    spaceSep :: TB.Builder
-    spaceSep = TB.singleton ' '
-
-    {- | Given a 'Date' construct a 'Text' 'TB.Builder'
-    corresponding to a Day\/Month\/Year encoding.
-
-    Example: @01 Jan 2020@
-    -}
-    builderDmy :: C.Date -> TB.Builder
-    builderDmy (C.Date (C.Year y) m d) =
-           zeroPadDayOfMonth d
-        <> spaceSep
-        <> TB.fromText (C.caseMonth C.abbreviated m)
-        <> spaceSep
-        <> TB.decimal y
-
-
-    zeroPadDayOfMonth :: C.DayOfMonth -> TB.Builder
-    zeroPadDayOfMonth (C.DayOfMonth d) =
-        if d < 100
-        then Vector.unsafeIndex twoDigitTextBuilder d
-        else TB.decimal d
-
-    twoDigitTextBuilder :: Vector.Vector TB.Builder
-    twoDigitTextBuilder = Vector.fromList $
-        map (TB.fromText . T.pack) twoDigitStrings
-    {-# NOINLINE twoDigitTextBuilder #-}
-
-    twoDigitStrings :: [String]
-    twoDigitStrings =
-        [ "00","01","02","03","04","05","06","07","08","09"
-        , "10","11","12","13","14","15","16","17","18","19"
-        , "20","21","22","23","24","25","26","27","28","29"
-        , "30","31","32","33","34","35","36","37","38","39"
-        , "40","41","42","43","44","45","46","47","48","49"
-        , "50","51","52","53","54","55","56","57","58","59"
-        , "60","61","62","63","64","65","66","67","68","69"
-        , "70","71","72","73","74","75","76","77","78","79"
-        , "80","81","82","83","84","85","86","87","88","89"
-        , "90","91","92","93","94","95","96","97","98","99"
-        ]
+showTimeOffset :: C.ZonedTime -> Text
+showTimeOffset = T.pack . C.formatTime C.defaultTimeLocale "[%d %b %Y %H:%M:%S%3Q %Ez] "
 
 ----------------------------------------------------------------------------
 -- Utility functions
 ----------------------------------------------------------------------------
 
+{- | Shows a thread id in the following format:
+
+__>>__ showThreadId <$> Control.Concurrent.myThreadId
+"[ThreadId 4898] "
+-}
 showThreadId :: ThreadId -> Text
 showThreadId = square . T.pack . show
 
diff --git a/src/Colog/Monad.hs b/src/Colog/Monad.hs
--- a/src/Colog/Monad.hs
+++ b/src/Colog/Monad.hs
@@ -1,15 +1,15 @@
 {-# LANGUAGE InstanceSigs #-}
 
 {- |
-Copyright:  (c) 2018-2020 Kowainik
+Copyright:  (c) 2018-2022 Kowainik, 2023-2025 Co-Log
 SPDX-License-Identifier: MPL-2.0
-Maintainer: Kowainik <xrom.xkov@gmail.com>
 
 Core of the @mtl@ implementation.
 -}
 
 module Colog.Monad
        ( LoggerT (..)
+       , HasLog (..)
        , WithLog
        , logMsg
        , logMsgs
@@ -21,20 +21,22 @@
 import Prelude hiding (log)
 
 import Control.Monad.IO.Class (MonadIO (..))
+import Control.Monad.IO.Unlift (MonadUnliftIO (..))
 import Control.Monad.Reader (MonadReader (..), ReaderT (..), asks)
 import Control.Monad.Trans.Class (MonadTrans (..))
 import Data.Foldable (traverse_)
 import GHC.Stack (HasCallStack)
 
-import Colog.Core (HasLog (..), LogAction (..), overLogAction, hoistLogAction)
+import Colog.Core (HasLog (..), LogAction (..), hoistLogAction, overLogAction)
 
 
 {- | @newtype@ wrapper 'ReaderT' that keeps 'LogAction' in its context.
 -}
 newtype LoggerT msg m a = LoggerT
     { runLoggerT :: ReaderT (LogAction (LoggerT msg m) msg) m a
-    } deriving newtype ( Functor, Applicative, Monad, MonadIO
+    } deriving newtype ( Functor, Applicative, Monad, MonadIO, MonadFail
                        , MonadReader (LogAction (LoggerT msg m) msg)
+                       , MonadUnliftIO
                        )
 
 instance MonadTrans (LoggerT msg) where
diff --git a/src/Colog/Pure.hs b/src/Colog/Pure.hs
--- a/src/Colog/Pure.hs
+++ b/src/Colog/Pure.hs
@@ -1,7 +1,6 @@
 {- |
-Copyright:  (c) 2018-2020 Kowainik
+Copyright:  (c) 2018-2022 Kowainik, 2023-2025 Co-Log
 SPDX-License-Identifier: MPL-2.0
-Maintainer: Kowainik <xrom.xkov@gmail.com>
 
 Pure implementation of logging action.
 -}
@@ -16,6 +15,8 @@
        , logMessagePure
        ) where
 
+import Control.Monad.Catch (MonadThrow)
+import Control.Monad.IO.Class (MonadIO)
 import Control.Monad.State (MonadState, StateT (..), modify')
 import Control.Monad.Trans.Class (MonadTrans)
 import Data.Bifunctor (second)
@@ -31,7 +32,9 @@
 -}
 newtype PureLoggerT msg m a = PureLoggerT
     { runPureLoggerT :: StateT (Seq msg) m a
-    } deriving newtype (Functor, Applicative, Monad, MonadTrans, MonadState (Seq msg))
+    } deriving newtype ( Functor, Applicative, Monad, MonadTrans
+                       , MonadState (Seq msg), MonadFail, MonadIO, MonadThrow
+                       )
 
 -- | Returns result value of 'PureLoggerT' and list of logged messages.
 runPureLogT :: Functor m => PureLoggerT msg m a -> m (a, [msg])
diff --git a/src/Colog/Rotation.hs b/src/Colog/Rotation.hs
--- a/src/Colog/Rotation.hs
+++ b/src/Colog/Rotation.hs
@@ -1,7 +1,6 @@
 {- |
-Copyright:  (c) 2018-2020 Kowainik
+Copyright:  (c) 2018-2022 Kowainik, 2023-2025 Co-Log
 SPDX-License-Identifier: MPL-2.0
-Maintainer: Kowainik <xrom.xkov@gmail.com>
 Stability:  experimental
 
 __NOTE:__ This functionality is not to be considered stable
@@ -125,7 +124,7 @@
 
 -- if you give it name like `node.log.4` then it returns `Just 4`
 logFileIndex :: FilePath -> Maybe Natural
-logFileIndex path = fmap NE.tail (nonEmpty (POS.takeExtension path)) >>= readMaybe
+logFileIndex path = nonEmpty (POS.takeExtension path) >>= readMaybe . NE.tail
 
 -- creates list of files with indices who are older on given Limit than the latest one
 getOldFiles :: Limit -> FilePath -> IO [FilePath]
diff --git a/test/Doctest.hs b/test/Doctest.hs
new file mode 100644
--- /dev/null
+++ b/test/Doctest.hs
@@ -0,0 +1,22 @@
+module Main(main) where
+
+import System.FilePath.Glob (glob)
+import Test.DocTest (doctest)
+
+main :: IO ()
+main = do
+    sourceFiles <- glob "src/**/*.hs"
+    doctest
+        $ "-XConstraintKinds"
+        : "-XDerivingStrategies"
+        : "-XDeriveGeneric"
+        : "-XGeneralizedNewtypeDeriving"
+        : "-XLambdaCase"
+        : "-XOverloadedStrings"
+        : "-XRecordWildCards"
+        : "-XScopedTypeVariables"
+        : "-XStandaloneDeriving"
+        : "-XTupleSections"
+        : "-XTypeApplications"
+        : "-XViewPatterns"
+        : sourceFiles
diff --git a/test/Property.hs b/test/Property.hs
--- a/test/Property.hs
+++ b/test/Property.hs
@@ -1,9 +1,8 @@
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE TemplateHaskell  #-}
 
-module Main where
+module Main (main) where
 
-import Data.Semigroup ((<>))
 import Hedgehog (MonadGen, Property, checkSequential, discover, forAll, property, (===))
 import System.Exit (exitFailure, exitSuccess)
 import System.IO (BufferMode (..), hSetBuffering, stderr, stdout)
diff --git a/tutorials/1-intro/Intro.lhs b/tutorials/1-intro/Intro.lhs
--- a/tutorials/1-intro/Intro.lhs
+++ b/tutorials/1-intro/Intro.lhs
@@ -6,7 +6,7 @@
 You can run this tutorial by executing the following command:
 
 ```shell
-cabal new-run tutorial-intro
+cabal new-run tutorial-intro --flag=tutorial
 ```
 
 ## Preamble: imports and language extensions
diff --git a/tutorials/2-custom/Custom.lhs b/tutorials/2-custom/Custom.lhs
deleted file mode 100644
--- a/tutorials/2-custom/Custom.lhs
+++ /dev/null
@@ -1,136 +0,0 @@
-# Using a custom monad that stores `LogAction` inside its environment
-
-This tutorial covers the more advanced topic of using the `co-log` library with
-a custom application monad.
-
-You can run this tutorial by executing the following command:
-
-```shell
-cabal new-run tutorial-custom
-```
-
-## Preamble: imports and language extensions
-
-Since this is a literate Haskell file, we need to specify all our language
-extensions and imports up front.
-
-```haskell
-{-# LANGUAGE DerivingStrategies    #-}
-{-# LANGUAGE FlexibleContexts      #-}
-{-# LANGUAGE FlexibleInstances     #-}
-{-# LANGUAGE InstanceSigs          #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE PatternSynonyms       #-}
-
-import Prelude hiding (log)
-
-import Colog (pattern D, HasLog (..), pattern I, LogAction, Message, WithLog, log,
-              richMessageAction)
-import Control.Monad.IO.Class (MonadIO)
-import Control.Monad.Reader (MonadReader, ReaderT (..))
-```
-
-## Application environment
-
-If you have a complex Haskell application, then you are likely to also have
-non-trivial settings that configure your application environment. The
-environment may store various parameters relevant to your application's
-behavior. Interestingly, we can store a `LogAction` inside the same
-environment to use it for our logging functions.
-
-The environment for your application may look like this:
-
-```haskell
-data Env m = Env
-    { envServerPort :: !Int
-    , envLogAction  :: !(LogAction m Message)
-    }
-```
-
-Several notes about this data type:
-
-1. It stores different parameters, like the server port.
-2. It stores a `LogAction` that can log values of the `Message` type
-from `co-log` in the `m` monad.
-3. `Env` is parameterized by type variable `m` which is going to be the
-application monad.
-
-The next step is to define an instance of the `HasLog` typeclass for the `Env`
-data type. This instance will specify how to get and update the `LogAction`
-stored inside the environment.
-
-```haskell
-instance HasLog (Env m) Message m where
-    getLogAction :: Env m -> LogAction m Message
-    getLogAction = envLogAction
-    {-# INLINE getLogAction #-}
-
-    setLogAction :: LogAction m Message -> Env m -> Env m
-    setLogAction newLogAction env = env { envLogAction = newLogAction }
-    {-# INLINE setLogAction #-}
-```
-
-That's it! `co-log` requires very little boilerplate.
-
-## Application monad
-
-Now let's define our application monad:
-
-```haskell
-newtype App a = App
-    { unApp :: ReaderT (Env App) IO a
-    } deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader (Env App))
-```
-
-This monad stores `Env` parameterized by the monad itself in its context.
-Nothing special is required here to tell the monad how to use the logger.
-
-## Example
-
-`co-log` relies on the tagless final technique for writing functions. So you
-define your monadic actions with the `WithLog` constraint that allows you to
-perform logging:
-
-```haskell
-example :: WithLog env Message m => m ()
-example = do
-    log D "First message..."
-    log I "Second message..."
-```
-
-The `WithLog` constraint has three type parameters: the application environment,
-the type of the message and the monad. Function `log` takes two parameters:
-the logger severity and the log message's text.
-
-## Running example
-
-Now we are ready to execute this action.
-
-First, let's create an example environment:
-
-```haskell
-simpleEnv :: Env App
-simpleEnv = Env
-    { envServerPort = 8081
-    , envLogAction  = richMessageAction
-    }
-```
-
-Then we need to define a function that performs actions of type `App`:
-
-```haskell
-runApp :: Env App -> App a -> IO a
-runApp env app = runReaderT (unApp app) env
-```
-
-Putting it all together, we can specialize the `WithLog` constraint to our
-`App` monad and run our example.
-
-```haskell
-main :: IO ()
-main = runApp simpleEnv example
-```
-
-And the output will look like this:
-
-![co-log output example](https://user-images.githubusercontent.com/8126674/71579355-1b804780-2af4-11ea-97eb-3f220011fd8d.png)
diff --git a/tutorials/2-loggert/loggert.lhs b/tutorials/2-loggert/loggert.lhs
new file mode 100644
--- /dev/null
+++ b/tutorials/2-loggert/loggert.lhs
@@ -0,0 +1,116 @@
+# Simple Message and LoggerT
+
+This tutorial will show you how to use LoggerT and Simple message to log with more information in a more flexible way.
+
+You can run this tutorial by executing the following command:
+
+```shell
+cabal new-run tutorial-loggert-simple --flag=tutorial
+```
+
+## Preamble: imports and language extensions
+
+Since this is a literate Haskell file, we need to specify all our language
+extensions and imports up front.
+
+```haskell
+{-# OPTIONS_GHC -Wno-unused-top-binds #-}
+{-# LANGUAGE DataKinds                  #-}
+{-# LANGUAGE DerivingStrategies         #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE OverloadedStrings          #-}
+
+module Main (main) where
+
+import Control.Monad.Reader
+import GHC.Stack
+import Prelude hiding (log)
+import Data.Text (Text,append)
+import Colog ( LogAction, SimpleMsg(..), usingLoggerT, LoggerT, (<&),
+              WithLog, cmap, logText,fmtSimpleMessage,formatWith,
+              logTextStderr,logTextStdout, getLogAction
+              )
+```
+
+## LoggerT
+
+The `LoggerT` monad transformer wraps a ReaderT that keeps `LogAction` in its context. It denotes a
+computation of logging. 
+The `WithLog` constraint has three type parameters: the application environment,
+the type of the message and the monad. The actions constraint by `WithLog` could be used as `LoggerT`.
+
+In this example, we use a `LoggerT` monad transformer and a monadic action with `WithLog` constraint to perform log (the preferred way).
+
+```haskell
+-- logTextExample1 asks a logger from the LoggerT monad transformer, and then writes the text into the LogAction
+-- it needs `HasCallStack` to print the stack information correctly
+logTextExample1 :: (HasCallStack, Monad m) => LoggerT SimpleMsg m ()
+logTextExample1 = 
+    asks getLogAction >>= \logger ->
+        logger <& SimpleMsg{ 
+            simpleMsgStack = callStack
+            , simpleMsgText = "this is a demo log for simple message!" 
+            }
+
+-- logTextExample2 logs the text down with the respective call stack information by the logger carried by env
+-- logTextExample2 is an equivalent version of LoggerT as logTextExample1 with more features so we recommend you to use it
+logTextExample2 :: WithLog env SimpleMsg m => m ()
+logTextExample2 = do
+    logText "you see the demo log for simple message again!"
+```
+
+
+## Simple Message
+
+The simple message is data type without `severity`. It contains a callstack information and a text message.
+
+```idris
+data SimpleMsg = SimpleMsg
+    { simpleMsgStack :: !CallStack
+    , simpleMsgText  :: !Text
+    }
+```
+
+When logging, simple messages require a format for transforming from simple messages to text. We can either use `formatWith` or its alias `cmap`
+to combine it with the `LogAction`.
+
+```haskell
+logStdoutAction :: LogAction IO SimpleMsg
+logStdoutAction = cmap fmtSimpleMessage logTextStdout
+
+logStdErrAction :: LogAction IO SimpleMsg
+logStdErrAction = formatWith fmtSimpleMessage logTextStderr
+```
+
+What's more, it's possible to define an own formatter.
+
+```haskell
+selfDefinedFmtSimpleMessage :: SimpleMsg -> Text
+selfDefinedFmtSimpleMessage = append "+ self defined behavior: " . fmtSimpleMessage
+
+logByOwnFormatterAction :: LogAction IO SimpleMsg
+logByOwnFormatterAction = formatWith selfDefinedFmtSimpleMessage logTextStderr
+```
+
+## Running example
+
+Now we are ready to execute those actions defined above.
+
+```haskell
+main :: IO ()
+main = do
+    usingLoggerT logStdoutAction logTextExample1
+    usingLoggerT logStdoutAction logTextExample2
+    usingLoggerT logStdErrAction logTextExample1
+    usingLoggerT logStdErrAction logTextExample2
+    usingLoggerT logByOwnFormatterAction logTextExample1
+    usingLoggerT logByOwnFormatterAction logTextExample2
+```
+
+Run command `cabal new-run tutorial-loggert-simple --flag=tutorial`.
+
+And the output will look like this:
+
+![](../img/2-loggert-output.jpg)
diff --git a/tutorials/3-loggert-with-message/loggert.lhs b/tutorials/3-loggert-with-message/loggert.lhs
new file mode 100644
--- /dev/null
+++ b/tutorials/3-loggert-with-message/loggert.lhs
@@ -0,0 +1,117 @@
+# Message and LoggerT
+
+The previous tutorial shows how to use `LoggerT` with simple message.
+This tutorial will show you how to use LoggerT together with messages with more details(e.g. severity).
+
+You can run this tutorial by executing the following command:
+
+```shell
+cabal new-run tutorial-loggert
+```
+
+## Preamble: imports and language extensions
+
+```haskell
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# OPTIONS_GHC -Wno-unused-top-binds #-}
+
+module Main (main) where
+
+import Colog (
+    LogAction,
+    Message,
+    Msg (..),
+    WithLog,
+    cmap,
+    fmtMessage,
+    formatWith,
+    logError,
+    logInfo,
+    logTextStderr,
+    logTextStdout,
+    msgSeverity,
+    msgText,
+    richMessageAction,
+    showSeverity,
+    usingLoggerT,
+ )
+
+import Data.Text (Text)
+import Prelude hiding (log)
+```
+
+## LoggerT
+
+The `LoggerT` monad transformer wraps a ReaderT that keeps `LogAction` in its context. It denotes a
+computation of logging. So you define your monadic actions with the `WithLog` constraint that allows you to perform logging.
+The `Message` allows you to log with severity.
+
+```haskell
+example1 :: WithLog env Message m => m ()
+example1 = do
+    logInfo "this is a demo log for message!"
+
+example2 :: WithLog env Message m => m ()
+example2 = do
+    logError "Mayday! Mayday! Here is an example of error log\n"
+```
+
+The `WithLog` constraint has three type parameters:
+
+- `env` – application environment
+- `msg` – type of the message
+- `m` – monad
+
+The actions constraint by `WithLog` could be used as `LoggerT`.
+
+## Message
+
+The message is parametrized by the `Severity` type through `Msg`, which is a general logging message data type.
+
+```idris
+type Message = Msg Severity
+data Msg sev = Msg
+    { msgSeverity :: !sev
+    , msgStack    :: !CallStack
+    , msgText     ::  Text
+    }
+```
+
+Hence, besides printing the callstack, as the `LoggerT` with simple message tutorial shows, this example supports printing severity and thread id as well.
+
+```haskell
+logStdoutAction :: LogAction IO Message
+logStdoutAction = cmap fmtMessage logTextStdout
+
+fmtMessageWithoutSourceLoc :: Message -> Text
+fmtMessageWithoutSourceLoc Msg{..} =
+    showSeverity msgSeverity
+        <> msgText
+
+logStdErrActionWithoutStackAction :: LogAction IO Message
+logStdErrActionWithoutStackAction = formatWith fmtMessageWithoutSourceLoc logTextStderr
+```
+
+## Running example
+
+Now we are ready to execute those actions defined above.
+
+```haskell
+main :: IO ()
+main = do
+    usingLoggerT logStdoutAction example1
+    usingLoggerT logStdoutAction example2
+    usingLoggerT logStdErrActionWithoutStackAction example1
+    usingLoggerT logStdErrActionWithoutStackAction example2
+    usingLoggerT richMessageAction example1
+    usingLoggerT richMessageAction example2
+```
+
+Run command `cabal new-run tutorial-loggert --flag=tutorial`, and the output will look like this:
+
+![](../img/3-loggert-message.jpg)
diff --git a/tutorials/Concurrent.hs b/tutorials/Concurrent.hs
--- a/tutorials/Concurrent.hs
+++ b/tutorials/Concurrent.hs
@@ -1,4 +1,4 @@
-{- | The purpose of this module is to check concurrent abilities of @colog@.
+{- | The purpose of this module is to check concurrent abilities of @co-log@.
 -}
 module Main (main) where
 
@@ -6,7 +6,6 @@
 import Control.Monad (forM_, replicateM_, void)
 import Data.ByteString (ByteString)
 import Data.ByteString.Char8 (pack)
-import Data.Semigroup ((<>))
 import Prelude hiding (log)
 
 import Colog (LogAction, logByteStringStderr, logByteStringStdout, (<&))
diff --git a/tutorials/Main.hs b/tutorials/Main.hs
--- a/tutorials/Main.hs
+++ b/tutorials/Main.hs
@@ -11,7 +11,6 @@
 {-# LANGUAGE OverloadedStrings          #-}
 {-# LANGUAGE PatternSynonyms            #-}
 {-# LANGUAGE TypeApplications           #-}
-{-# LANGUAGE TypeSynonymInstances       #-}
 
 module Main (main) where
 
@@ -21,17 +20,16 @@
 import Control.Exception (Exception)
 import Control.Monad.IO.Class (MonadIO (..))
 import Control.Monad.Reader (MonadReader, ReaderT (..))
-import Data.Semigroup ((<>))
 
-import Colog (pattern D, HasLog (..), LogAction, Message, Msg (..), PureLogger, RichMsg (..),
-              SimpleMsg (..), WithLog, cmap, cmapM, defaultFieldMap, fmtMessage,
-              fmtRichMessageDefault, fmtSimpleRichMessageDefault, liftLogIO, log, logException,
-              logInfo, logMessagePure, logMsg, logMsgs, logPrint, logStringStdout, logText,
-              logTextStderr, logTextStdout, logWarning, runPureLog, upgradeMessageAction,
-              usingLoggerT, withLog, withLogTextFile, (*<), (<&), (>$), (>$<), (>*), (>*<), (>|<))
-
-import qualified Data.TypeRepMap as TM
+import Colog (HasLog (..), LogAction, Message, Msg (..), PureLogger, RichMsg (..), SimpleMsg (..),
+              WithLog, cmap, cmapM, defaultFieldMap, fmtMessage, fmtRichMessageDefault,
+              fmtSimpleRichMessageDefault, liftLogIO, log, logException, logInfo, logMessagePure,
+              logMsg, logMsgs, logPrint, logStringStdout, logText, logTextStderr, logTextStdout,
+              logWarning, pattern D, runPureLog, upgradeMessageAction, usingLoggerT, withLog,
+              withLogTextFile, (*<), (<&), (>$), (>$<), (>*), (>*<), (>|<))
 
+import Data.Dependent.Map (delete)
+import Type.Reflection (typeRep)
 
 example :: WithLog env Message m => m ()
 example = do
@@ -46,7 +44,7 @@
 app :: (WithLog env Message m, MonadIO m) => m ()
 app = do
     logWarning "Starting application..."
-    liftIO $ threadDelay $ 10^(6 :: Int)
+    liftIO $ threadDelay $ 10 ^ (6 :: Int)
     withLog (cmap addApp) $ do
         example
         exceptionL
@@ -170,7 +168,7 @@
 ----------------------------------------------------------------------------
 
 main :: IO ()
-main = withLogTextFile "co-log/tutorials/example.log" $ \logTextFile -> do
+main = withLogTextFile "tutorials/example.log" $ \logTextFile -> do
     let runApp :: LogAction IO Message -> IO ()
         runApp action = usingLoggerT action app
 
@@ -190,7 +188,7 @@
 
     let fullMessageAction = upgradeMessageAction defaultFieldMap richMessageAction
     let semiMessageAction = upgradeMessageAction
-                                (TM.delete @"threadId" defaultFieldMap)
+                                (delete (typeRep @"threadId") defaultFieldMap)
                                 richMessageAction
 
     runApp simpleMessageAction
diff --git a/tutorials/custom/Custom.lhs b/tutorials/custom/Custom.lhs
new file mode 100644
--- /dev/null
+++ b/tutorials/custom/Custom.lhs
@@ -0,0 +1,136 @@
+# Using a custom monad that stores `LogAction` inside its environment
+
+This tutorial covers the more advanced topic of using the `co-log` library with
+a custom application monad.
+
+You can run this tutorial by executing the following command:
+
+```shell
+cabal new-run tutorial-custom
+```
+
+## Preamble: imports and language extensions
+
+Since this is a literate Haskell file, we need to specify all our language
+extensions and imports up front.
+
+```haskell
+{-# LANGUAGE DerivingStrategies    #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE InstanceSigs          #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE PatternSynonyms       #-}
+
+import Prelude hiding (log)
+
+import Colog (pattern D, HasLog (..), pattern I, LogAction, Message, WithLog, log,
+              richMessageAction)
+import Control.Monad.IO.Class (MonadIO)
+import Control.Monad.Reader (MonadReader, ReaderT (..))
+```
+
+## Application environment
+
+If you have a complex Haskell application, then you are likely to also have
+non-trivial settings that configure your application environment. The
+environment may store various parameters relevant to your application's
+behavior. Interestingly, we can store a `LogAction` inside the same
+environment to use it for our logging functions.
+
+The environment for your application may look like this:
+
+```haskell
+data Env m = Env
+    { envServerPort :: !Int
+    , envLogAction  :: !(LogAction m Message)
+    }
+```
+
+Several notes about this data type:
+
+1. It stores different parameters, like the server port.
+2. It stores a `LogAction` that can log values of the `Message` type
+from `co-log` in the `m` monad.
+3. `Env` is parameterized by type variable `m` which is going to be the
+application monad.
+
+The next step is to define an instance of the `HasLog` typeclass for the `Env`
+data type. This instance will specify how to get and update the `LogAction`
+stored inside the environment.
+
+```haskell
+instance HasLog (Env m) Message m where
+    getLogAction :: Env m -> LogAction m Message
+    getLogAction = envLogAction
+    {-# INLINE getLogAction #-}
+
+    setLogAction :: LogAction m Message -> Env m -> Env m
+    setLogAction newLogAction env = env { envLogAction = newLogAction }
+    {-# INLINE setLogAction #-}
+```
+
+That's it! `co-log` requires very little boilerplate.
+
+## Application monad
+
+Now let's define our application monad:
+
+```haskell
+newtype App a = App
+    { unApp :: ReaderT (Env App) IO a
+    } deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader (Env App))
+```
+
+This monad stores `Env` parameterized by the monad itself in its context.
+Nothing special is required here to tell the monad how to use the logger.
+
+## Example
+
+`co-log` relies on the tagless final technique for writing functions. So you
+define your monadic actions with the `WithLog` constraint that allows you to
+perform logging:
+
+```haskell
+example :: WithLog env Message m => m ()
+example = do
+    log D "First message..."
+    log I "Second message..."
+```
+
+The `WithLog` constraint has three type parameters: the application environment,
+the type of the message and the monad. Function `log` takes two parameters:
+the logger severity and the log message's text.
+
+## Running example
+
+Now we are ready to execute this action.
+
+First, let's create an example environment:
+
+```haskell
+simpleEnv :: Env App
+simpleEnv = Env
+    { envServerPort = 8081
+    , envLogAction  = richMessageAction
+    }
+```
+
+Then we need to define a function that performs actions of type `App`:
+
+```haskell
+runApp :: Env App -> App a -> IO a
+runApp env app = runReaderT (unApp app) env
+```
+
+Putting it all together, we can specialize the `WithLog` constraint to our
+`App` monad and run our example.
+
+```haskell
+main :: IO ()
+main = runApp simpleEnv example
+```
+
+And the output will look like this:
+
+![co-log output example](https://user-images.githubusercontent.com/8126674/71579355-1b804780-2af4-11ea-97eb-3f220011fd8d.png)
