diff --git a/little-logger.cabal b/little-logger.cabal
--- a/little-logger.cabal
+++ b/little-logger.cabal
@@ -3,11 +3,9 @@
 -- This file has been generated from package.yaml by hpack version 0.34.4.
 --
 -- see: https://github.com/sol/hpack
---
--- hash: 110790790f5c519685b242fae0c899af7c2e6b43e4528a46e120286c9f8410c0
 
 name:           little-logger
-version:        0.3.2
+version:        1.0.0
 synopsis:       Basic logging based on co-log
 description:    Please see the README on GitHub at <https://github.com/ejconlon/little-logger#readme>
 category:       Logging
@@ -29,19 +27,20 @@
 library
   exposed-modules:
       LittleLogger
-      LittleLogger.Manual
-      LittleLogger.Reader
   other-modules:
-      LittleLogger.Common
       Paths_little_logger
   hs-source-dirs:
       src
+  default-extensions:
+      ConstraintKinds
+      DerivingStrategies
+      DerivingVia
+      GeneralizedNewtypeDeriving
   ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints -fno-warn-unused-top-binds
   build-depends:
       base >=4.12 && <5
-    , co-log >=0.4 && <1
-    , co-log-core >=0.2 && <1
     , microlens >=0.4 && <1
+    , monad-logger ==0.3.*
     , mtl >=2.2 && <3
     , text >=1.2 && <2
     , unliftio-core >=0.1.2.0 && <2
@@ -54,14 +53,18 @@
       Paths_little_logger
   hs-source-dirs:
       test
+  default-extensions:
+      ConstraintKinds
+      DerivingStrategies
+      DerivingVia
+      GeneralizedNewtypeDeriving
   ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints -fno-warn-unused-top-binds -threaded -rtsopts -with-rtsopts=-N
   build-depends:
       base >=4.12 && <5
-    , co-log >=0.4 && <1
-    , co-log-core >=0.2 && <1
     , directory >=1.3.6.0 && <1.4
     , little-logger
     , microlens >=0.4 && <1
+    , monad-logger ==0.3.*
     , mtl >=2.2 && <3
     , tasty >=1.2.3 && <2
     , tasty-hunit >=0.10.0.2 && <1
diff --git a/src/LittleLogger.hs b/src/LittleLogger.hs
--- a/src/LittleLogger.hs
+++ b/src/LittleLogger.hs
@@ -1,12 +1,119 @@
-{-# LANGUAGE ConstraintKinds #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE Rank2Types #-}
-
--- | Basic logging based on co-log. Meant to give you what you need to get started with a single module import.
--- The root module exports 'LittleLogger.Reader' for use with RIO-style stacks, but if you want to manually
--- pass a 'SimpleLogAction' around, import 'LittleLogger.Manual' instead.
 module LittleLogger
-  ( module LittleLogger.Reader
+  ( textLogStr
+  , LogAction (..)
+  , defaultLogAction
+  , filterActionSeverity
+  , newLogAction
+  , runLogAction
+  , handleLogAction
+  , openLoggingHandle
+  , closeLoggingHandle
+  , fileLogAction
+  , HasLogAction (..)
+  , WithLogAction
+  , askLogAction
+  , LogActionWrapperM (..)
+  , LogActionT (..)
+  , runLogActionT
+  , LogActionM
+  , runLogActionM
+  -- Re-exports
+  , MonadLogger (..)
+  , Loc (..)
+  , LogSource
+  , LogLevel (..)
+  , LogStr
+  , ToLogStr (..)
+  , logDebugN
+  , logInfoN
+  , logWarnN
+  , logErrorN
+  , logOtherN
   ) where
 
-import LittleLogger.Reader
+import Control.Monad.IO.Class (MonadIO (..))
+import Control.Monad.IO.Unlift (MonadUnliftIO, askRunInIO)
+import Control.Monad.Logger.CallStack (Loc (..), LogLevel (..), LogSource, LogStr, MonadLogger (..), ToLogStr (..),
+                                       defaultOutput, fromLogStr, logDebugN, logErrorN, logInfoN, logOtherN, logWarnN)
+import Control.Monad.Reader (MonadReader, ReaderT (..), asks)
+import Data.Text (Text)
+import Data.Text.Encoding (decodeUtf8)
+import Lens.Micro (Lens')
+import Lens.Micro.Extras (view)
+import System.IO (BufferMode (LineBuffering), Handle, IOMode (AppendMode), hClose, hSetBuffering, openFile, stderr,
+                  withFile)
+
+textLogStr :: LogStr -> Text
+textLogStr = decodeUtf8 . fromLogStr
+
+newtype LogAction = LogAction { unLogAction :: Loc -> LogSource -> LogLevel -> LogStr -> IO () }
+
+instance Semigroup LogAction where
+  LogAction act1 <> LogAction act2 = LogAction (\loc src lvl msg -> act1 loc src lvl msg >> act2 loc src lvl msg)
+
+instance Monoid LogAction where
+  mempty = LogAction (\_ _ _ _ -> pure ())
+  mappend = (<>)
+
+newLogAction :: MonadUnliftIO m => (Loc -> LogSource -> LogLevel -> LogStr -> m ()) -> m LogAction
+newLogAction act = fmap (\run -> LogAction (\loc src lvl msg -> run (act loc src lvl msg))) askRunInIO
+
+runLogAction :: MonadIO m => LogAction -> Loc -> LogSource -> LogLevel -> LogStr -> m ()
+runLogAction (LogAction act) loc src lvl msg = liftIO (act loc src lvl msg)
+
+defaultLogAction :: LogAction
+defaultLogAction = LogAction (defaultOutput stderr)
+
+filterActionSeverity :: LogLevel -> LogAction -> LogAction
+filterActionSeverity lim (LogAction act) = LogAction (\loc src lvl msg -> if lvl >= lim then act loc src lvl msg else pure ())
+
+handleLogAction :: Handle -> LogAction
+handleLogAction = LogAction . defaultOutput
+
+openLoggingHandle :: MonadIO m => FilePath -> m Handle
+openLoggingHandle fp = do
+  handle <- liftIO (openFile fp AppendMode)
+  liftIO (hSetBuffering handle LineBuffering)
+  pure handle
+
+closeLoggingHandle :: MonadIO m => Handle -> m ()
+closeLoggingHandle = liftIO . hClose
+
+fileLogAction :: MonadUnliftIO m => FilePath -> (LogAction -> m a) -> m a
+fileLogAction fp f = do
+  run <- askRunInIO
+  liftIO $ withFile fp AppendMode $ \handle -> do
+    hSetBuffering handle LineBuffering
+    run (f (handleLogAction handle))
+
+class HasLogAction env where
+  logActionL :: Lens' env LogAction
+
+instance HasLogAction LogAction where
+  logActionL = id
+
+type WithLogAction env m = (MonadIO m, MonadReader env m, HasLogAction env)
+
+askLogAction :: (MonadReader env m, HasLogAction env) => m LogAction
+askLogAction = asks (view logActionL)
+
+-- | Use deriving-via with this wrapper to add MonadLogger instances to your types
+newtype LogActionWrapperM env m a = LogActionM { unLogActionM :: m a }
+  deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader env)
+
+instance WithLogAction env m => MonadLogger (LogActionWrapperM env m) where
+  monadLoggerLog loc src lvl msg = do
+    LogAction act <- askLogAction
+    liftIO (act loc src lvl (toLogStr msg))
+
+newtype LogActionT m a = LogActionT { unLogActionT :: ReaderT LogAction m a }
+  deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader LogAction)
+  deriving MonadLogger via (LogActionWrapperM LogAction (LogActionT m))
+
+runLogActionT :: LogActionT m a -> LogAction -> m a
+runLogActionT = runReaderT . unLogActionT
+
+type LogActionM a = LogActionT IO a
+
+runLogActionM :: LogActionM a -> LogAction -> IO a
+runLogActionM = runLogActionT
diff --git a/src/LittleLogger/Common.hs b/src/LittleLogger/Common.hs
deleted file mode 100644
--- a/src/LittleLogger/Common.hs
+++ /dev/null
@@ -1,62 +0,0 @@
-module LittleLogger.Common
-  ( LogAction (..)
-  , Message
-  , Msg (..)
-  , Severity (..)
-  , SimpleLogAction
-  , defaultSimpleLogAction
-  , filterActionSeverity
-  , newSimpleLogAction
-  , runLogAction
-  , runSimpleLogAction
-  , handleSimpleLogAction
-  , openLoggingHandle
-  , closeLoggingHandle
-  , fileSimpleLogAction
-  ) where
-
-import Colog.Actions (logByteStringHandle, richMessageAction)
-import Colog.Core.Action (LogAction (..), cmapM)
-import Colog.Core.Severity (Severity (..))
-import Colog.Message (Message, Msg (..), defaultFieldMap, fmtRichMessageDefault, upgradeMessageAction)
-import Control.Monad.IO.Class (MonadIO (..))
-import Control.Monad.IO.Unlift (MonadUnliftIO, askRunInIO)
-import Data.Text.Encoding (encodeUtf8)
-import System.IO (BufferMode (LineBuffering), Handle, IOMode (AppendMode), hClose, hSetBuffering, openFile, withFile)
-
-type SimpleLogAction = LogAction IO Message
-
-newSimpleLogAction :: MonadUnliftIO m => (Message -> m ()) -> m SimpleLogAction
-newSimpleLogAction f = fmap (\run -> LogAction (run . f)) askRunInIO
-
-runSimpleLogAction :: MonadIO m => SimpleLogAction -> Message -> m ()
-runSimpleLogAction = runLogAction
-
-runLogAction :: MonadIO m => LogAction IO msg -> msg -> m ()
-runLogAction (LogAction actIO) = liftIO . actIO
-
-defaultSimpleLogAction :: SimpleLogAction
-defaultSimpleLogAction = richMessageAction
-
-filterActionSeverity :: Severity -> SimpleLogAction -> SimpleLogAction
-filterActionSeverity lim (LogAction f) = LogAction (\msg -> if msgSeverity msg >= lim then f msg else pure ())
-
-handleSimpleLogAction :: Handle -> SimpleLogAction
-handleSimpleLogAction handle =  upgradeMessageAction defaultFieldMap $
-    cmapM (fmap encodeUtf8 . fmtRichMessageDefault) (logByteStringHandle handle)
-
-openLoggingHandle :: MonadIO m => FilePath -> m Handle
-openLoggingHandle fp = do
-  handle <- liftIO (openFile fp AppendMode)
-  liftIO (hSetBuffering handle LineBuffering)
-  pure handle
-
-closeLoggingHandle :: MonadIO m => Handle -> m ()
-closeLoggingHandle = liftIO . hClose
-
-fileSimpleLogAction :: MonadUnliftIO m => FilePath -> (SimpleLogAction -> m a) -> m a
-fileSimpleLogAction fp f = do
-  run <- askRunInIO
-  liftIO $ withFile fp AppendMode $ \handle -> do
-    hSetBuffering handle LineBuffering
-    run (f (handleSimpleLogAction handle))
diff --git a/src/LittleLogger/Manual.hs b/src/LittleLogger/Manual.hs
deleted file mode 100644
--- a/src/LittleLogger/Manual.hs
+++ /dev/null
@@ -1,39 +0,0 @@
--- | A logging "backend" where you pass around the log action manually.
-module LittleLogger.Manual
-  ( module LittleLogger.Common
-  , logMsg
-  , logDebug
-  , logError
-  , logException
-  , logInfo
-  , logWarning
-  , logWithSeverity
-  ) where
-
-import Control.Exception (Exception, displayException)
-import Control.Monad.IO.Class (MonadIO)
-import Data.Text (Text)
-import qualified Data.Text as Text
-import GHC.Stack (HasCallStack, callStack, withFrozenCallStack)
-import LittleLogger.Common
-
-logMsg :: MonadIO m => SimpleLogAction -> Message -> m ()
-logMsg = runSimpleLogAction
-
-logWithSeverity :: (MonadIO m, HasCallStack) => SimpleLogAction -> Severity -> Text -> m ()
-logWithSeverity act sev txt = withFrozenCallStack (logMsg act Msg { msgStack = callStack, msgSeverity = sev, msgText = txt })
-
-logDebug :: (MonadIO m, HasCallStack) => SimpleLogAction -> Text -> m ()
-logDebug act = withFrozenCallStack (logWithSeverity act Debug)
-
-logInfo :: (MonadIO m, HasCallStack) => SimpleLogAction -> Text -> m ()
-logInfo act = withFrozenCallStack (logWithSeverity act Info)
-
-logWarning :: (MonadIO m, HasCallStack) => SimpleLogAction -> Text -> m ()
-logWarning act = withFrozenCallStack (logWithSeverity act Warning)
-
-logError :: (MonadIO m, HasCallStack) => SimpleLogAction -> Text -> m ()
-logError act = withFrozenCallStack (logWithSeverity act Error)
-
-logException :: (MonadIO m, Exception e, HasCallStack) => SimpleLogAction -> e -> m ()
-logException act = withFrozenCallStack (logError act . Text.pack . displayException)
diff --git a/src/LittleLogger/Reader.hs b/src/LittleLogger/Reader.hs
deleted file mode 100644
--- a/src/LittleLogger/Reader.hs
+++ /dev/null
@@ -1,65 +0,0 @@
-{-# LANGUAGE ConstraintKinds #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE Rank2Types #-}
-
--- | A logging "backend" where you get the log action from the environment.
-module LittleLogger.Reader
-  ( module LittleLogger.Common
-  , HasSimpleLog (..)
-  , WithSimpleLog
-  , logMsg
-  , logDebug
-  , logError
-  , logException
-  , logInfo
-  , logWarning
-  , logWithSeverity
-  , runWithSimpleLogAction
-  ) where
-
-import Control.Exception (Exception, displayException)
-import Control.Monad.IO.Class (MonadIO (..))
-import Control.Monad.Reader (MonadReader (..), ReaderT (..))
-import Data.Text (Text)
-import qualified Data.Text as Text
-import GHC.Stack (HasCallStack, callStack, withFrozenCallStack)
-import Lens.Micro (Lens')
-import Lens.Micro.Extras (view)
-import LittleLogger.Common
-
-class HasSimpleLog env where
-  simpleLogL :: Lens' env SimpleLogAction
-
-instance HasSimpleLog SimpleLogAction where
-  simpleLogL = id
-
-type WithSimpleLog env m = (MonadIO m, MonadReader env m, HasSimpleLog env, HasCallStack)
-
-logMsg :: WithSimpleLog env m => Message -> m ()
-logMsg msg = do
-  env <- ask
-  let LogAction act = view simpleLogL env
-  liftIO (act msg)
-
-logWithSeverity :: WithSimpleLog env m => Severity -> Text -> m ()
-logWithSeverity sev txt = withFrozenCallStack (logMsg Msg { msgStack = callStack, msgSeverity = sev, msgText = txt })
-
-logDebug :: WithSimpleLog env m => Text -> m ()
-logDebug = withFrozenCallStack (logWithSeverity Debug)
-
-logInfo :: WithSimpleLog env m => Text -> m ()
-logInfo = withFrozenCallStack (logWithSeverity Info)
-
-logWarning :: WithSimpleLog env m => Text -> m ()
-logWarning = withFrozenCallStack (logWithSeverity Warning)
-
-logError :: WithSimpleLog env m => Text -> m ()
-logError = withFrozenCallStack (logWithSeverity Error)
-
-logException :: (WithSimpleLog env m, Exception e) => e -> m ()
-logException = withFrozenCallStack (logError . Text.pack . displayException)
-
--- | Usually 'm' will be some kind of 'Reader' monad. In the case where you don't care what it
--- is and you only need to do logging and IO, you can use this.
-runWithSimpleLogAction :: SimpleLogAction -> (forall env m. WithSimpleLog env m => m a) -> IO a
-runWithSimpleLogAction = flip runReaderT
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,46 +1,44 @@
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE Rank2Types #-}
 
 module Main (main) where
 
 import Control.Exception (finally)
 import Data.IORef (IORef, modifyIORef', newIORef, readIORef)
 import Data.Text (Text)
-import LittleLogger (LogAction (LogAction), Msg (msgSeverity, msgText), Severity (..), SimpleLogAction, WithSimpleLog,
-                     fileSimpleLogAction, filterActionSeverity, logDebug, logError, logInfo, logWarning,
-                     runWithSimpleLogAction)
+import LittleLogger (LogAction (..), LogActionM, LogLevel (..), fileLogAction, filterActionSeverity, logDebugN,
+                     logErrorN, logInfoN, logWarnN, runLogActionM, textLogStr)
 import System.Directory (removeFile)
 import System.IO.Temp (emptySystemTempFile)
 import Test.Tasty (TestTree, defaultMain, testGroup)
 import Test.Tasty.HUnit (testCase, (@?=))
 
-emitLogs :: WithSimpleLog env m => m ()
+emitLogs :: LogActionM ()
 emitLogs = do
-  logDebug "debug"
-  logInfo "info"
-  logWarning "warning"
-  logError "error"
+  logDebugN "debug"
+  logInfoN "info"
+  logWarnN "warn"
+  logErrorN "error"
 
-refAction :: IORef [(Severity, Text)] -> SimpleLogAction
-refAction ref = LogAction (\msg -> modifyIORef' ref (++ [(msgSeverity msg, msgText msg)]))
+refAction :: IORef [(LogLevel, Text)] -> LogAction
+refAction ref = LogAction (\_ _ lvl msg -> modifyIORef' ref (++ [(lvl, textLogStr msg)]))
 
-runWithRefAction :: (SimpleLogAction -> SimpleLogAction) -> (forall env m. WithSimpleLog env m => m ()) -> IO [(Severity, Text)]
+runWithRefAction :: (LogAction -> LogAction) -> (LogActionM ()) -> IO [(LogLevel, Text)]
 runWithRefAction f m = do
   ref <- newIORef []
   let action = f (refAction ref)
-  runWithSimpleLogAction action m
+  runLogActionM m action
   readIORef ref
 
-expectedFiltered :: [(Severity, Text)]
+expectedFiltered :: [(LogLevel, Text)]
 expectedFiltered =
-  [ (Warning, "warning")
-  , (Error, "error")
+  [ (LevelWarn, "warn")
+  , (LevelError, "error")
   ]
 
-expectedUnfiltered :: [(Severity, Text)]
+expectedUnfiltered :: [(LogLevel, Text)]
 expectedUnfiltered =
-  [ (Debug, "debug")
-  , (Info, "info")
+  [ (LevelDebug, "debug")
+  , (LevelInfo, "info")
   ] ++ expectedFiltered
 
 testUnfiltered :: TestTree
@@ -50,17 +48,17 @@
 
 testFiltered :: TestTree
 testFiltered = testCase "Filtered" $ do
-  actual <- runWithRefAction (filterActionSeverity Warning) emitLogs
+  actual <- runWithRefAction (filterActionSeverity LevelWarn) emitLogs
   actual @?= expectedFiltered
 
 testFile :: TestTree
 testFile = testCase "File" $ do
   fp <- emptySystemTempFile "little-logger-test"
   flip finally (removeFile fp) $ do
-    fileSimpleLogAction fp (`runWithSimpleLogAction` emitLogs)
+    fileLogAction fp (runLogActionM emitLogs)
     firstContents <- readFile fp
     length (lines firstContents) @?= 4
-    fileSimpleLogAction fp (`runWithSimpleLogAction` emitLogs)
+    fileLogAction fp (runLogActionM emitLogs)
     secondContents <- readFile fp
     length (lines secondContents) @?= 8
 
