Skip to content

Commit f810ada

Browse files
authoredJun 3, 2022
First draft
1 parent e655793 commit f810ada

30 files changed

+1277
-0
lines changed
 

‎.editorconfig

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Stop traversing up parent directories here
2+
root = true
3+
4+
[*]
5+
charset = utf-8
6+
end_of_line = lf
7+
insert_final_newline = true
8+
trim_trailing_whitespace = true
9+
10+
[*.hs]
11+
indent_size = 2
12+
indent_style = space
13+
max_line_length = 80

‎.github/workflows/ci.yml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: main
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
12+
strategy:
13+
matrix:
14+
stack-yaml:
15+
- stack-nightly.yaml # ghc-9.2
16+
- stack.yaml # ghc-9.0
17+
- stack-lts-18.28.yaml # ghc-8.10
18+
fail-fast: false
19+
20+
steps:
21+
- uses: actions/checkout@v3
22+
- uses: freckle/stack-cache-action@v2
23+
with:
24+
stack-yaml: ${{ matrix.stack-yaml }}
25+
- uses: freckle/stack-action@v3
26+
with:
27+
stack-yaml: ${{ matrix.stack-yaml }}
28+
29+
lint:
30+
runs-on: ubuntu-latest
31+
steps:
32+
- uses: actions/checkout@v2
33+
- uses: rwe/actions-hlint-setup@v1
34+
- uses: rwe/actions-hlint-run@v2
35+
with:
36+
fail-on: warning

‎.github/workflows/release.yml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches: main
6+
7+
jobs:
8+
release:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v2
12+
13+
- id: tag
14+
if: false # Remove when ready to release
15+
uses: freckle/haskell-tag-action@v1
16+
env:
17+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18+
19+
- if: steps.tag.outputs.tag
20+
uses: freckle/stack-upload-action@v2
21+
env:
22+
HACKAGE_API_KEY: ${{ secrets.HACKAGE_UPLOAD_API_KEY }}

‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.stack-work

‎.stylish-haskell.yaml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
steps:
3+
- simple_align:
4+
cases: false
5+
top_level_patterns: false
6+
records: false
7+
- imports:
8+
align: none
9+
list_align: after_alias
10+
pad_module_names: false
11+
long_list_align: new_line_multiline
12+
empty_list_align: right_after
13+
list_padding: 2
14+
separate_lists: false
15+
space_surround: false
16+
- language_pragmas:
17+
style: vertical
18+
align: false
19+
remove_redundant: false
20+
- trailing_whitespace: {}
21+
columns: 80
22+
newline: native
23+
# For multi-package repositories this default-extensions must be set manually.
24+
# For single package repos it can be inferred from the cabal file.
25+
cabal: true

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO

‎LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2022 Renaissance Learning Inc
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

‎README.lhs

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Logging
2+
3+
A batteries-included Structured Logging toolkit for writing to a single logging
4+
abstraction in CLI apps and production services in Haskell.
5+
6+
## Simple Usage
7+
8+
<!--
9+
```haskell
10+
module Main (module Main) where
11+
12+
import Prelude
13+
14+
import Data.Aeson
15+
import Data.Text (Text)
16+
import Text.Markdown.Unlit ()
17+
```
18+
-->
19+
20+
```haskell
21+
import Logging.Simple
22+
```
23+
24+
Throughout your application, you should write against the ubiquitous
25+
`MonadLogger` interface, but using [`monad-logger-aeson`][monad-logger-aeson]:
26+
27+
[monad-logger-aeson]: https://jship.github.io/posts/2022-05-17-announcing-monad-logger-aeson/
28+
29+
```haskell
30+
action :: MonadLogger m => m ()
31+
action = do
32+
logInfo "This is a message sans details"
33+
34+
logError
35+
$ "Something went wrong"
36+
:# [ "error" .= object
37+
[ "code" .= (100 :: Int)
38+
, "messages" .= (["x", "y"] :: [Text])
39+
]
40+
]
41+
42+
logDebug "This won't be seen in default settings"
43+
```
44+
45+
When you run your transformer stack, wrap it in `runLoggerLoggingT` providing
46+
any value with a `HasLogger` instance (such as your main `App`). The `Logger`
47+
type itself has such an instance, and we provide `runSimpleLoggingT` for the
48+
simplest case: it creates one configured via environment variables and then
49+
calls `runLoggerLoggingT` with it.
50+
51+
You can use `withThreadContext` (from `monad-logger-aeson`) to add details that
52+
will appear in all the logged messages within that scope. Placing one of these
53+
at the very top-level adds details to all logged messages.
54+
55+
```haskell
56+
runner :: LoggingT IO a -> IO a
57+
runner = runSimpleLoggingT . withThreadContext ["app" .= ("example" :: Text)]
58+
59+
main :: IO ()
60+
main = runner action
61+
```
62+
63+
The defaults are good for CLI applications, producing colorful output (if
64+
connected to a terminal device) suitable for a human:
65+
66+
![](files/readme-terminal.png)
67+
68+
Under the hood, `Logging.Settings.Env` is using [`envparse`][envparse] to
69+
configure logging through environment variables. See that module for full
70+
details. One thing we can adjust is `LOG_LEVEL`:
71+
72+
[envparse]: https://hackage.haskell.org/package/envparse
73+
74+
![](files/readme-terminal-debug.png)
75+
76+
In production, you will probably want to set `LOG_FORMAT=json` and ship logs to
77+
some aggregator like Datadog or Mezmo (formerly LogDNA):
78+
79+
![](files/readme-terminal-json.png)
80+
81+
## Advanced Usage
82+
83+
TODO
84+
85+
## Integration with RIO
86+
87+
TODO
88+
89+
## Integration with Amazonka
90+
91+
```hs
92+
data App = App
93+
{ appLogger :: Logger
94+
, appAWS :: AWS.Env
95+
}
96+
97+
instance HasLogger App where
98+
-- ...
99+
100+
runApp :: ReaderT App (LoggingT IO) a -> IO a
101+
runApp f = do
102+
logger <- newLogger defaultLogSettings
103+
app <- App logger <$> runLoggerLoggingT logger awsDiscover
104+
runLoggerLoggingT app $ runReaderT f app
105+
106+
awsDiscover :: (MonadIO m, MonadLoggerIO m) => m AWS.Env
107+
awsDiscover = do
108+
monadLoggerLog <- askLoggerIO
109+
110+
env <- liftIO $ AWS.newEnv AWS.discover
111+
pure $ env
112+
{ AWS.envLogger = \level msg -> do
113+
monadLoggerLog
114+
defaultLoc -- TODO: there may be a way to get a CallStack/Loc
115+
"Amazonka"
116+
(\case
117+
AWS.Info -> LevelInfo
118+
AWS.Error -> LevelError
119+
AWS.Debug -> LevelDebug
120+
AWS.Trace -> LevelDebug
121+
)
122+
(toLogStr msg)
123+
}
124+
```
125+
126+
## Integration with WAI
127+
128+
```hs
129+
import Network.Wai.Middleware.Logging
130+
131+
instance HasLogger App where
132+
-- ...
133+
134+
waiMiddleware :: App -> Middleware
135+
waiMiddleware app = requestLogger app . defaultMiddlewaresNoLogging
136+
```
137+
138+
## Integration with Warp
139+
140+
```hs
141+
instance HasLogger App where
142+
-- ...
143+
144+
warpSettings :: App -> Settings
145+
warpSettings app = setOnException onEx $ defaultSettings
146+
where
147+
onEx _req ex =
148+
when (defaultShouldDisplayException ex)
149+
$ runLoggerLoggingT app
150+
$ logError
151+
$ "Warp exception"
152+
:# ["exception" .= displayException ex]
153+
```
154+
155+
## Integration with Yesod
156+
157+
```hs
158+
import Logging.Logger (getLoggerLoggerSet)
159+
160+
instance HasLogger App where
161+
-- ...
162+
163+
instance Yesod App where
164+
-- ...
165+
makeLogger App {..} = do
166+
logger <- defaultMakeLogger
167+
pure $ logger { Y.loggerSet = getLoggerLoggerSet appLogger }
168+
169+
messageLoggerSource app _logger loc source level msg =
170+
runLoggerLoggingT app $ monadLoggerLog loc source level msg
171+
```
172+
173+
---
174+
175+
[LICENSE](./LICENSE) | [CHANGELOG](./CHANGELOG.md)

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
README.lhs

‎brittany.yaml

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
conf_debug:
3+
dconf_roundtrip_exactprint_only: false
4+
dconf_dump_bridoc_simpl_par: false
5+
dconf_dump_ast_unknown: false
6+
dconf_dump_bridoc_simpl_floating: false
7+
dconf_dump_config: false
8+
dconf_dump_bridoc_raw: false
9+
dconf_dump_bridoc_final: false
10+
dconf_dump_bridoc_simpl_alt: false
11+
dconf_dump_bridoc_simpl_indent: false
12+
dconf_dump_annotations: false
13+
dconf_dump_bridoc_simpl_columns: false
14+
dconf_dump_ast_full: false
15+
conf_forward:
16+
options_ghc:
17+
- -XBangPatterns
18+
- -XDataKinds
19+
- -XDeriveAnyClass
20+
- -XDeriveFoldable
21+
- -XDeriveFunctor
22+
- -XDeriveGeneric
23+
- -XDeriveLift
24+
- -XDeriveTraversable
25+
- -XDerivingStrategies
26+
- -XFlexibleContexts
27+
- -XFlexibleInstances
28+
- -XGADTs
29+
- -XGeneralizedNewtypeDeriving
30+
- -XLambdaCase
31+
- -XMultiParamTypeClasses
32+
- -XNoImplicitPrelude
33+
- -XNoMonomorphismRestriction
34+
- -XOverloadedStrings
35+
- -XRankNTypes
36+
- -XRecordWildCards
37+
- -XScopedTypeVariables
38+
- -XStandaloneDeriving
39+
- -XTypeApplications
40+
- -XTypeFamilies
41+
conf_errorHandling:
42+
econf_ExactPrintFallback: ExactPrintFallbackModeInline
43+
econf_Werror: false
44+
econf_omit_output_valid_check: false
45+
econf_produceOutputOnErrors: false
46+
conf_preprocessor:
47+
ppconf_CPPMode: CPPModeAbort
48+
ppconf_hackAroundIncludes: false
49+
conf_obfuscate: false
50+
conf_roundtrip_exactprint_only: false
51+
conf_version: 1
52+
conf_layout:
53+
lconfig_reformatModulePreamble: true
54+
lconfig_altChooser:
55+
tag: AltChooserBoundedSearch
56+
contents: 3
57+
lconfig_allowSingleLineExportList: false
58+
lconfig_importColumn: 60
59+
lconfig_hangingTypeSignature: false
60+
lconfig_importAsColumn: 50
61+
lconfig_alignmentLimit: 1
62+
lconfig_indentListSpecial: true
63+
lconfig_indentAmount: 2
64+
lconfig_alignmentBreakOnMultiline: true
65+
lconfig_cols: 80
66+
lconfig_indentPolicy: IndentPolicyLeft
67+
lconfig_indentWhereSpecial: true
68+
lconfig_columnAlignMode:
69+
tag: ColumnAlignModeDisabled
70+
contents: 0.7

‎files/readme-json.png

49 KB
Loading

‎files/readme-terminal-debug.png

46.9 KB
Loading

‎files/readme-terminal.png

36.7 KB
Loading

0 commit comments

Comments
 (0)