├── .travis.yml
├── .gitignore
├── .vscode
└── settings.json
├── .prettierrc
├── docs
├── modules
│ ├── index.md
│ ├── Task.ts.md
│ ├── index.ts.md
│ └── IO.ts.md
├── _config.yml
└── index.md
├── tsconfig.build.json
├── tsconfig.build-es6.json
├── tslint.json
├── jest.config.js
├── tsconfig.json
├── CHANGELOG.md
├── LICENSE
├── src
├── Task.ts
├── index.ts
└── IO.ts
├── test
├── task.ts
└── index.ts
├── package.json
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '10'
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | node_modules
3 | lib
4 | es6
5 | dev
6 | coverage
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "./node_modules/typescript/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "printWidth": 120,
5 | "arrowParens": "always"
6 | }
7 |
--------------------------------------------------------------------------------
/docs/modules/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Modules
3 | has_children: true
4 | permalink: /docs/modules
5 | nav_order: 2
6 | ---
7 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "noEmit": false
5 | },
6 | "include": ["./src"]
7 | }
8 |
--------------------------------------------------------------------------------
/tsconfig.build-es6.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.build.json",
3 | "compilerOptions": {
4 | "outDir": "./es6",
5 | "module": "es6"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | remote_theme: pmarsceill/just-the-docs
2 |
3 | # Enable or disable the site search
4 | search_enabled: true
5 |
6 | # Aux links for the upper right navigation
7 | aux_links:
8 | 'logging-ts on GitHub':
9 | - 'https://github.com/gcanti/logging-ts'
10 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint-config-standard",
3 | "rules": {
4 | "space-before-function-paren": false,
5 | "no-use-before-declare": false,
6 | "variable-name": false,
7 | "quotemark": [true, "single", "jsx-double"],
8 | "ter-indent": false,
9 | "strict-boolean-expressions": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | testEnvironment: 'node',
4 | collectCoverage: true,
5 | collectCoverageFrom: ['src/**/*.ts'],
6 | transform: {
7 | '^.+\\.tsx?$': 'ts-jest'
8 | },
9 | testRegex: 'test',
10 | moduleFileExtensions: ['ts', 'js'],
11 | coverageThreshold: {
12 | global: {
13 | branches: 100,
14 | functions: 100,
15 | lines: 100,
16 | statements: 100
17 | }
18 | },
19 | modulePathIgnorePatterns: ['property-test']
20 | }
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noEmit": true,
4 | "outDir": "./lib",
5 | "target": "es5",
6 | "module": "commonjs",
7 | "moduleResolution": "node",
8 | "lib": ["es6", "dom"],
9 | "sourceMap": false,
10 | "declaration": true,
11 | "strict": true,
12 | "noImplicitReturns": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "forceConsistentCasingInFileNames": true
17 | },
18 | "include": ["./src", "./test"]
19 | }
20 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | > **Tags:**
4 | >
5 | > - [New Feature]
6 | > - [Bug Fix]
7 | > - [Breaking Change]
8 | > - [Documentation]
9 | > - [Internal]
10 | > - [Polish]
11 | > - [Experimental]
12 |
13 | **Note**: Gaps between patch versions are faulty/broken releases.
14 | **Note**: A feature tagged as Experimental is in a high state of flux, you're at risk of it changing without notice.
15 |
16 | # 0.3.4
17 |
18 | - **New Feature**
19 | - `IO`
20 | - add `withLogger` combinator, #21 (@waynevanson)
21 |
22 | # 0.3.3
23 |
24 | - **New Feature**
25 | - add `Logger2`, #19 (@tetsuo)
26 |
27 | # 0.3.2
28 |
29 | - **New Feature**
30 | - add async logger, #18 (@mstn)
31 |
32 | # 0.3.1
33 |
34 | - **New Feature**
35 | - add build in ES6 format (@gcanti)
36 |
37 | # 0.3.0
38 |
39 | - **Breaking Change**
40 | - upgrade to `fp-ts@2.x` (@gcanti)
41 |
42 | # 0.2.0
43 |
44 | - **Breaking Change**
45 | - move `fp-ts` to `peerDependencies` (@mstn)
46 |
47 | # 0.1.0
48 |
49 | - **Breaking Change**
50 | - upgrade to `fp-ts@1.x.x` (@gcanti)
51 |
52 | # 0.0.1
53 |
54 | Initial release
55 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-2018 Giulio Canti
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.
22 |
--------------------------------------------------------------------------------
/src/Task.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 0.3.2
3 | */
4 |
5 | import { Contravariant1 } from 'fp-ts/lib/Contravariant'
6 | import { Predicate } from 'fp-ts/lib/function'
7 | import { Task, task } from 'fp-ts/lib/Task'
8 | import { Monoid } from 'fp-ts/lib/Monoid'
9 | import { pipeable } from 'fp-ts/lib/pipeable'
10 |
11 | import { getLoggerM } from '.'
12 |
13 | const T = getLoggerM(task)
14 |
15 | declare module 'fp-ts/lib/HKT' {
16 | interface URItoKind {
17 | LoggerTask: LoggerTask
18 | }
19 | }
20 |
21 | /**
22 | * @since 0.3.2
23 | */
24 | export const URI = 'LoggerTask'
25 |
26 | /**
27 | * @since 0.3.2
28 | */
29 | export type URI = typeof URI
30 |
31 | /**
32 | * @since 0.3.2
33 | */
34 | export interface LoggerTask {
35 | (a: A): Task
36 | }
37 |
38 | /**
39 | * @since 0.3.2
40 | */
41 | export const filter: (logger: LoggerTask, predicate: Predicate) => LoggerTask = T.filter
42 |
43 | /**
44 | * @since 0.3.2
45 | */
46 | export const getMonoid: () => Monoid> = T.getMonoid
47 |
48 | /**
49 | * @since 0.3.2
50 | */
51 | export const loggerTask: Contravariant1 = {
52 | URI,
53 | contramap: T.contramap
54 | }
55 |
56 | const { contramap } = pipeable(loggerTask)
57 |
58 | export {
59 | /**
60 | * @since 0.3.2
61 | */
62 | contramap
63 | }
64 |
--------------------------------------------------------------------------------
/test/task.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert'
2 |
3 | import * as T from 'fp-ts/lib/Task'
4 |
5 | import * as C from 'fp-ts/lib/Console'
6 | import * as L from '../src/Task'
7 |
8 | describe('TaskLogger', () => {
9 | let ledger: Array
10 | let consolelog: any
11 |
12 | const asyncLog = (s: unknown) => T.fromIO(C.log(s))
13 |
14 | beforeEach(() => {
15 | ledger = []
16 | consolelog = console.log
17 | console.log = (message: any) => ledger.push(message)
18 | })
19 |
20 | afterEach(() => {
21 | console.log = consolelog
22 | })
23 |
24 | it('contramap', () => {
25 | const logger = L.loggerTask.contramap(asyncLog, (s: string) => s.length)
26 | logger('a')()
27 | assert.deepEqual(ledger, [1])
28 | })
29 |
30 | it('getSemigroup', () => {
31 | const S = L.getMonoid()
32 | const logger = S.concat(asyncLog, asyncLog)
33 | logger('a')()
34 | assert.deepEqual(ledger, ['a', 'a'])
35 | })
36 |
37 | it('getMonoid', () => {
38 | const M = L.getMonoid()
39 | const logger = M.concat(asyncLog, M.empty)
40 | logger('a')()
41 | assert.deepEqual(ledger, ['a'])
42 | })
43 |
44 | it('filter', () => {
45 | const logger = L.filter(asyncLog, (a: string) => a.length > 2)
46 | logger('a')()
47 | logger('aaa')()
48 | assert.deepEqual(ledger, ['aaa'])
49 | })
50 | })
51 |
--------------------------------------------------------------------------------
/docs/modules/Task.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Task.ts
3 | nav_order: 3
4 | parent: Modules
5 | ---
6 |
7 | # Task overview
8 |
9 | Added in v0.3.2
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [LoggerTask (interface)](#loggertask-interface)
16 | - [URI (type alias)](#uri-type-alias)
17 | - [URI](#uri)
18 | - [contramap](#contramap)
19 | - [filter](#filter)
20 | - [getMonoid](#getmonoid)
21 | - [loggerTask](#loggertask)
22 |
23 | ---
24 |
25 | # LoggerTask (interface)
26 |
27 | **Signature**
28 |
29 | ```ts
30 | export interface LoggerTask {
31 | (a: A): Task
32 | }
33 | ```
34 |
35 | Added in v0.3.2
36 |
37 | # URI (type alias)
38 |
39 | **Signature**
40 |
41 | ```ts
42 | export type URI = typeof URI
43 | ```
44 |
45 | Added in v0.3.2
46 |
47 | # URI
48 |
49 | **Signature**
50 |
51 | ```ts
52 | export const URI: "LoggerTask" = ...
53 | ```
54 |
55 | Added in v0.3.2
56 |
57 | # contramap
58 |
59 | **Signature**
60 |
61 | ```ts
62 | (f: (b: B) => A) => (fa: LoggerTask) => LoggerTask
63 | ```
64 |
65 | Added in v0.3.2
66 |
67 | # filter
68 |
69 | **Signature**
70 |
71 | ```ts
72 | export const filter: (logger: LoggerTask, predicate: Predicate) => LoggerTask = ...
73 | ```
74 |
75 | Added in v0.3.2
76 |
77 | # getMonoid
78 |
79 | **Signature**
80 |
81 | ```ts
82 | export const getMonoid: () => Monoid> = ...
83 | ```
84 |
85 | Added in v0.3.2
86 |
87 | # loggerTask
88 |
89 | **Signature**
90 |
91 | ```ts
92 | export const loggerTask: Contravariant1 = ...
93 | ```
94 |
95 | Added in v0.3.2
96 |
--------------------------------------------------------------------------------
/test/index.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert'
2 | import * as C from 'fp-ts/lib/Console'
3 | import * as L from '../src/IO'
4 | import { withLogger } from '../src/IO'
5 | import * as IO from 'fp-ts/lib/IO'
6 | import { pipe } from 'fp-ts/lib/pipeable'
7 |
8 | describe('Logger', () => {
9 | let ledger: Array
10 | let consolelog: any
11 |
12 | beforeEach(() => {
13 | ledger = []
14 | consolelog = console.log
15 | console.log = (message: any) => ledger.push(message)
16 | })
17 |
18 | afterEach(() => {
19 | console.log = consolelog
20 | })
21 |
22 | it('contramap', () => {
23 | const logger = L.loggerIO.contramap(C.log, (s: string) => s.length)
24 | logger('a')()
25 | assert.deepEqual(ledger, [1])
26 | })
27 |
28 | it('getSemigroup', () => {
29 | const S = L.getMonoid()
30 | const logger = S.concat(C.log, C.log)
31 | logger('a')()
32 | assert.deepEqual(ledger, ['a', 'a'])
33 | })
34 |
35 | it('getMonoid', () => {
36 | const M = L.getMonoid()
37 | const logger = M.concat(C.log, M.empty)
38 | logger('a')()
39 | assert.deepEqual(ledger, ['a'])
40 | })
41 |
42 | it('filter', () => {
43 | const logger = L.filter(C.log, (a: string) => a.length > 2)
44 | logger('a')()
45 | logger('aaa')()
46 | assert.deepEqual(ledger, ['aaa'])
47 | })
48 | })
49 |
50 | describe('withLogger', () => {
51 | let ledger: Array
52 | let consolelog: any
53 |
54 | beforeEach(() => {
55 | ledger = []
56 | consolelog = console.log
57 | console.log = (message: any) => ledger.push(message)
58 | })
59 |
60 | afterEach(() => {
61 | console.log = consolelog
62 | })
63 |
64 | describe('sync', () => {
65 | test('MonadIO1', () => {
66 | const ioLog = withLogger(IO.io)(C.log)
67 |
68 | const result = pipe(
69 | IO.of(2),
70 | ioLog((a) => a + 5)
71 | )()
72 |
73 | assert.equal(result, 2)
74 | assert.deepEqual(ledger, [7])
75 | })
76 | })
77 | })
78 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "logging-ts",
3 | "version": "0.3.4",
4 | "description": "Composable loggers for TypeScript",
5 | "files": [
6 | "lib",
7 | "es6"
8 | ],
9 | "main": "lib/index.js",
10 | "module": "es6/index.js",
11 | "typings": "lib/index.d.ts",
12 | "sideEffects": false,
13 | "scripts": {
14 | "lint": "tslint -p .",
15 | "jest": "jest",
16 | "prettier": "prettier --no-semi --single-quote --print-width 120 --parser typescript --list-different \"{src,test}/**/*.ts\"",
17 | "fix-prettier": "prettier --no-semi --single-quote --print-width 120 --parser typescript --write \"{src,test}/**/*.ts\"",
18 | "test": "npm run prettier && npm run jest && npm run docs",
19 | "clean": "rm -rf ./lib ./es6",
20 | "prebuild": "npm run clean",
21 | "build": "tsc -p ./tsconfig.build.json && tsc -p ./tsconfig.build-es6.json",
22 | "postbuild": "import-path-rewrite && prettier --write \"./{lib,es6}/**/*.ts\"",
23 | "prepublish": "npm run build",
24 | "docs": "docs-ts"
25 | },
26 | "repository": {
27 | "type": "git",
28 | "url": "https://github.com/gcanti/logging-ts.git"
29 | },
30 | "author": "Giulio Canti ",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/gcanti/logging-ts/issues"
34 | },
35 | "homepage": "https://github.com/gcanti/logging-ts",
36 | "peerDependencies": {
37 | "fp-ts": "^2.0.5"
38 | },
39 | "devDependencies": {
40 | "@types/jest": "^22.2.2",
41 | "@types/node": "^12.6.8",
42 | "docs-ts": "^0.3.4",
43 | "fp-ts": "^2.0.5",
44 | "import-path-rewrite": "github:gcanti/import-path-rewrite",
45 | "jest": "^24.8.0",
46 | "prettier": "^1.19.1",
47 | "ts-jest": "^24.0.2",
48 | "tslint": "5.9.1",
49 | "tslint-config-standard": "7.0.0",
50 | "typescript": "^3.7.2"
51 | },
52 | "tags": [
53 | "typescript",
54 | "logging",
55 | "functional-programming",
56 | "fp-ts"
57 | ],
58 | "keywords": [
59 | "typescript",
60 | "logging",
61 | "functional-programming",
62 | "fp-ts"
63 | ]
64 | }
65 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Home
3 | nav_order: 1
4 | ---
5 |
6 | Adapted from [purescript-logging](https://github.com/rightfold/purescript-logging)
7 |
8 | # TypeScript compatibility
9 |
10 | The stable version is tested against TypeScript 3.1.6, but should run with TypeScript 2.8.0+ too
11 |
12 | # Usage
13 |
14 | From `purescript-logging`'s README
15 |
16 | > A logger receives records and potentially performs some effects. You can create a logger from any function `(a: A) => HKT` for any `A` and `M`.
17 | >
18 | > Unlike most other logging libraries, `logging-ts` has no separate concepts "loggers" and "handlers". Instead, loggers
19 | > can be composed into larger loggers using the `Semigroup` instance. Loggers can also be transformed using `contramap`
20 | > (for transforming records) and `filter` (for filtering records). An example use case might be the following:
21 |
22 | ```ts
23 | import { io } from 'fp-ts/lib/IO'
24 | import * as L from 'logging-ts'
25 | import * as C from 'fp-ts/lib/Console'
26 | import * as D from 'fp-ts/lib/Date'
27 |
28 | type Level = 'Debug' | 'Info' | 'Warning' | 'Error'
29 |
30 | interface Entry {
31 | message: string
32 | time: Date
33 | level: Level
34 | }
35 |
36 | const showEntry = (entry: Entry): string => `[${entry.level}] ${entry.time.toLocaleString()} ${entry.message}`
37 |
38 | const getEntryLogger = (prefix: string) => {
39 | return new L.Logger((entry: Entry) => C.log(`${prefix}: ${showEntry(entry)}`))
40 | }
41 |
42 | const filter = L.filter(io)
43 | const debugLogger = filter(getEntryLogger('debug.log'), e => e.level === 'Debug')
44 | const productionLogger = filter(getEntryLogger('production.log'), e => e.level !== 'Debug')
45 | const logger = L.getSemigroup(io)().concat(debugLogger, productionLogger)
46 |
47 | const log = L.log(logger)
48 |
49 | const info = (message: string) => (time: Date) => log({ message, time, level: 'Info' })
50 | const debug = (message: string) => (time: Date) => log({ message, time, level: 'Debug' })
51 |
52 | const program = D.create
53 | .chain(info('boot'))
54 | .chain(() => D.create)
55 | .chain(debug('Hello!'))
56 |
57 | program.run()
58 | /*
59 | production.log: [Info] 2017-10-17 10:14:21 boot
60 | debug.log: [Debug] 2017-10-17 10:14:21 Hello!
61 | */
62 | ```
63 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Adapted from https://github.com/rightfold/purescript-logging
3 | *
4 | * @since 0.3.0
5 | */
6 | import { Applicative, Applicative1, Applicative2 } from 'fp-ts/lib/Applicative'
7 | import { Predicate } from 'fp-ts/lib/function'
8 | import { HKT, Kind, Kind2, URIS, URIS2 } from 'fp-ts/lib/HKT'
9 | import { Monoid } from 'fp-ts/lib/Monoid'
10 |
11 | /**
12 | * A logger receives records and potentially performs some effects
13 | *
14 | * @since 0.3.0
15 | */
16 | export interface Logger {
17 | (a: A): HKT
18 | }
19 |
20 | /**
21 | * @since 0.3.0
22 | */
23 | export interface LoggerM {
24 | readonly contramap: (fa: Logger, f: (b: B) => A) => Logger
25 | readonly filter: (logger: Logger, predicate: Predicate) => Logger
26 | readonly getMonoid: () => Monoid>
27 | }
28 |
29 | /**
30 | * @since 0.3.0
31 | */
32 | export interface Logger1 {
33 | (a: A): Kind
34 | }
35 |
36 | /**
37 | * @since 0.3.0
38 | */
39 | export interface LoggerM1 {
40 | readonly contramap: (fa: Logger1, f: (b: B) => A) => Logger1
41 | readonly filter: (logger: Logger1, predicate: Predicate) => Logger1
42 | readonly getMonoid: () => Monoid>
43 | }
44 |
45 | /**
46 | * @since 0.3.3
47 | */
48 | export interface Logger2 {
49 | (a: A): Kind2
50 | }
51 |
52 | /**
53 | * @since 0.3.3
54 | */
55 | export interface LoggerM2 {
56 | readonly contramap: (fa: Logger2, f: (b: B) => A) => Logger2
57 | readonly filter: (logger: Logger2, predicate: Predicate) => Logger2
58 | readonly getMonoid: () => Monoid>
59 | }
60 |
61 | /**
62 | * @since 0.3.0
63 | */
64 | export function getLoggerM(M: Applicative2): LoggerM2
65 | export function getLoggerM(M: Applicative1): LoggerM1
66 | export function getLoggerM(M: Applicative): LoggerM
67 | export function getLoggerM(M: Applicative): LoggerM {
68 | const empty = () => M.of(undefined)
69 | return {
70 | contramap: (fa, f) => (b) => fa(f(b)),
71 | filter: (ma, predicate) => (a) => (predicate(a) ? ma(a) : M.of(undefined)),
72 | getMonoid: () => ({
73 | concat: (x, y) => (a) =>
74 | M.ap(
75 | M.map(x(a), () => () => undefined),
76 | y(a)
77 | ),
78 | empty
79 | })
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Adapted from [purescript-logging](https://github.com/rightfold/purescript-logging)
2 |
3 | # `fp-ts` and TypeScript compatibility
4 |
5 | | `logging-ts` version | required `fp-ts` version | required TypeScript version |
6 | | -------------------- | ------------------------ | --------------------------- |
7 | | 0.3.0 | 2.0.0+ | 3.5+ |
8 | | 0.2.0 | 1.7.0+ | 2.8.0+ |
9 |
10 | # Usage
11 |
12 | From `purescript-logging`'s README
13 |
14 | > A logger receives records and potentially performs some effects. You can create a logger from any function `(a: A) => HKT` for any `A` and `M`.
15 | >
16 | > Unlike most other logging libraries, `logging-ts` has no separate concepts "loggers" and "handlers". Instead, loggers
17 | > can be composed into larger loggers using the `Semigroup` instance. Loggers can also be transformed using `contramap`
18 | > (for transforming records) and `filter` (for filtering records). An example use case might be the following:
19 |
20 | ```ts
21 | import * as C from 'fp-ts/lib/Console'
22 | import * as D from 'fp-ts/lib/Date'
23 | import { chain, IO } from 'fp-ts/lib/IO'
24 | import { pipe } from 'fp-ts/lib/pipeable'
25 | import * as L from '../src/IO'
26 |
27 | type Level = 'Debug' | 'Info' | 'Warning' | 'Error'
28 |
29 | interface Entry {
30 | message: string
31 | time: Date
32 | level: Level
33 | }
34 |
35 | function showEntry(entry: Entry): string {
36 | return `[${entry.level}] ${entry.time.toLocaleString()} ${entry.message}`
37 | }
38 |
39 | function getLoggerEntry(prefix: string): L.LoggerIO {
40 | return entry => C.log(`${prefix}: ${showEntry(entry)}`)
41 | }
42 |
43 | const debugLogger = L.filter(getLoggerEntry('debug.log'), e => e.level === 'Debug')
44 | const productionLogger = L.filter(getLoggerEntry('production.log'), e => e.level !== 'Debug')
45 | const logger = L.getMonoid().concat(debugLogger, productionLogger)
46 |
47 | const info = (message: string) => (time: Date): IO => logger({ message, time, level: 'Info' })
48 | const debug = (message: string) => (time: Date): IO => logger({ message, time, level: 'Debug' })
49 |
50 | const program = pipe(
51 | D.create,
52 | chain(info('boot')),
53 | chain(() => D.create),
54 | chain(debug('Hello!'))
55 | )
56 |
57 | program()
58 | /*
59 | production.log: [Info] 10/4/2019, 12:44:48 PM boot
60 | debug.log: [Debug] 10/4/2019, 12:44:48 PM Hello!
61 | */
62 | ```
63 |
64 | # Documentation
65 |
66 | - [API Reference](https://gcanti.github.io/logging-ts)
67 |
--------------------------------------------------------------------------------
/docs/modules/index.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: index.ts
3 | nav_order: 1
4 | parent: Modules
5 | ---
6 |
7 | # index overview
8 |
9 | Adapted from https://github.com/rightfold/purescript-logging
10 |
11 | Added in v0.3.0
12 |
13 | ---
14 |
15 | Table of contents
16 |
17 | - [Logger (interface)](#logger-interface)
18 | - [Logger1 (interface)](#logger1-interface)
19 | - [Logger2 (interface)](#logger2-interface)
20 | - [LoggerM (interface)](#loggerm-interface)
21 | - [LoggerM1 (interface)](#loggerm1-interface)
22 | - [LoggerM2 (interface)](#loggerm2-interface)
23 | - [getLoggerM](#getloggerm)
24 |
25 | ---
26 |
27 | # Logger (interface)
28 |
29 | A logger receives records and potentially performs some effects
30 |
31 | **Signature**
32 |
33 | ```ts
34 | export interface Logger {
35 | (a: A): HKT
36 | }
37 | ```
38 |
39 | Added in v0.3.0
40 |
41 | # Logger1 (interface)
42 |
43 | **Signature**
44 |
45 | ```ts
46 | export interface Logger1 {
47 | (a: A): Kind
48 | }
49 | ```
50 |
51 | Added in v0.3.0
52 |
53 | # Logger2 (interface)
54 |
55 | **Signature**
56 |
57 | ```ts
58 | export interface Logger2 {
59 | (a: A): Kind2
60 | }
61 | ```
62 |
63 | Added in v0.3.3
64 |
65 | # LoggerM (interface)
66 |
67 | **Signature**
68 |
69 | ```ts
70 | export interface LoggerM {
71 | readonly contramap: (fa: Logger, f: (b: B) => A) => Logger
72 | readonly filter: (logger: Logger, predicate: Predicate) => Logger
73 | readonly getMonoid: () => Monoid>
74 | }
75 | ```
76 |
77 | Added in v0.3.0
78 |
79 | # LoggerM1 (interface)
80 |
81 | **Signature**
82 |
83 | ```ts
84 | export interface LoggerM1 {
85 | readonly contramap: (fa: Logger1, f: (b: B) => A) => Logger1
86 | readonly filter: (logger: Logger1, predicate: Predicate) => Logger1
87 | readonly getMonoid: () => Monoid>
88 | }
89 | ```
90 |
91 | Added in v0.3.0
92 |
93 | # LoggerM2 (interface)
94 |
95 | **Signature**
96 |
97 | ```ts
98 | export interface LoggerM2 {
99 | readonly contramap: (fa: Logger2, f: (b: B) => A) => Logger2
100 | readonly filter: (logger: Logger2, predicate: Predicate) => Logger2
101 | readonly getMonoid: () => Monoid>
102 | }
103 | ```
104 |
105 | Added in v0.3.3
106 |
107 | # getLoggerM
108 |
109 | **Signature**
110 |
111 | ```ts
112 | export function getLoggerM(M: Applicative2): LoggerM2
113 | export function getLoggerM(M: Applicative1): LoggerM1
114 | export function getLoggerM(M: Applicative): LoggerM { ... }
115 | ```
116 |
117 | Added in v0.3.0
118 |
--------------------------------------------------------------------------------
/docs/modules/IO.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: IO.ts
3 | nav_order: 2
4 | parent: Modules
5 | ---
6 |
7 | # IO overview
8 |
9 | Added in v0.3.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [LoggerIO (interface)](#loggerio-interface)
16 | - [URI (type alias)](#uri-type-alias)
17 | - [URI](#uri)
18 | - [contramap](#contramap)
19 | - [filter](#filter)
20 | - [getMonoid](#getmonoid)
21 | - [loggerIO](#loggerio)
22 | - [withLogger](#withlogger)
23 |
24 | ---
25 |
26 | # LoggerIO (interface)
27 |
28 | **Signature**
29 |
30 | ```ts
31 | export interface LoggerIO {
32 | (a: A): IO
33 | }
34 | ```
35 |
36 | Added in v0.3.0
37 |
38 | # URI (type alias)
39 |
40 | **Signature**
41 |
42 | ```ts
43 | export type URI = typeof URI
44 | ```
45 |
46 | Added in v0.3.0
47 |
48 | # URI
49 |
50 | **Signature**
51 |
52 | ```ts
53 | export const URI: "LoggerIO" = ...
54 | ```
55 |
56 | Added in v0.3.0
57 |
58 | # contramap
59 |
60 | **Signature**
61 |
62 | ```ts
63 | (f: (b: B) => A) => (fa: LoggerIO) => LoggerIO
64 | ```
65 |
66 | Added in v0.3.0
67 |
68 | # filter
69 |
70 | **Signature**
71 |
72 | ```ts
73 | export const filter: (logger: LoggerIO, predicate: Predicate) => LoggerIO = ...
74 | ```
75 |
76 | Added in v0.3.0
77 |
78 | # getMonoid
79 |
80 | **Signature**
81 |
82 | ```ts
83 | export const getMonoid: () => Monoid> = ...
84 | ```
85 |
86 | Added in v0.3.0
87 |
88 | # loggerIO
89 |
90 | **Signature**
91 |
92 | ```ts
93 | export const loggerIO: Contravariant1 = ...
94 | ```
95 |
96 | Added in v0.3.0
97 |
98 | # withLogger
99 |
100 | **Signature**
101 |
102 | ```ts
103 | export function withLogger(
104 | M: MonadIO3
105 | ): (logger: LoggerIO) => (message: (a: A) => B) => (ma: Kind3) => Kind3
106 | export function withLogger(
107 | M: MonadIO2
108 | ): (logger: LoggerIO) => (message: (a: A) => B) => (ma: Kind2) => Kind2
109 | export function withLogger(
110 | M: MonadIO2C
111 | ): (logger: LoggerIO) => (message: (a: A) => B) => (ma: Kind2) => Kind2
112 | export function withLogger(
113 | M: MonadIO1
114 | ): (logger: LoggerIO) => (message: (a: A) => B) => (ma: Kind) => Kind
115 | export function withLogger(
116 | M: MonadIO
117 | ): (logger: LoggerIO) => (message: (a: A) => B) => (ma: HKT) => HKT { ... }
118 | ```
119 |
120 | **Example**
121 |
122 | ```ts
123 | import { pipe } from 'fp-ts/lib/pipeable'
124 | import * as IO from 'fp-ts/lib/IO'
125 | import * as C from 'fp-ts/lib/Console'
126 | import { withLogger } from 'logging-ts/lib/IO'
127 | import { equal } from 'assert'
128 |
129 | const log = withLogger(IO.io)(C.log)
130 |
131 | const result = pipe(
132 | IO.of(3),
133 | log(n => `lifted "${n}" to the IO monad`), // n === 3
134 | IO.map(n => n * n),
135 | log(n => `squared the value, which is "${n}"`) // n === 9
136 | )
137 |
138 | equal(result(), 9)
139 | ```
140 |
141 | Added in v0.3.4
142 |
--------------------------------------------------------------------------------
/src/IO.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 0.3.0
3 | */
4 | import { Contravariant1 } from 'fp-ts/lib/Contravariant'
5 | import { Predicate } from 'fp-ts/lib/function'
6 | import { IO, io } from 'fp-ts/lib/IO'
7 | import { Monoid } from 'fp-ts/lib/Monoid'
8 | import { pipeable } from 'fp-ts/lib/pipeable'
9 | import { getLoggerM } from '.'
10 | import { URIS3, Kind3, URIS2, Kind2, URIS, Kind, HKT } from 'fp-ts/lib/HKT'
11 | import { MonadIO3, MonadIO2C, MonadIO2, MonadIO1, MonadIO } from 'fp-ts/lib/MonadIO'
12 |
13 | const T = getLoggerM(io)
14 |
15 | declare module 'fp-ts/lib/HKT' {
16 | interface URItoKind {
17 | LoggerIO: LoggerIO
18 | }
19 | }
20 |
21 | /**
22 | * @since 0.3.0
23 | */
24 | export const URI = 'LoggerIO'
25 |
26 | /**
27 | * @since 0.3.0
28 | */
29 | export type URI = typeof URI
30 |
31 | /**
32 | * @since 0.3.0
33 | */
34 | export interface LoggerIO {
35 | (a: A): IO
36 | }
37 |
38 | /**
39 | * @since 0.3.0
40 | */
41 | export const filter: (logger: LoggerIO, predicate: Predicate) => LoggerIO = T.filter
42 |
43 | /**
44 | * @since 0.3.0
45 | */
46 | export const getMonoid: () => Monoid> = T.getMonoid
47 |
48 | /**
49 | * @since 0.3.0
50 | */
51 | export const loggerIO: Contravariant1 = {
52 | URI,
53 | contramap: T.contramap
54 | }
55 |
56 | /**
57 | * @category Combinator
58 | *
59 | * @since 0.3.4
60 | *
61 | * @example
62 | * import { pipe } from 'fp-ts/lib/pipeable'
63 | * import * as IO from 'fp-ts/lib/IO'
64 | * import * as C from 'fp-ts/lib/Console'
65 | * import { withLogger } from 'logging-ts/lib/IO'
66 | * import { equal } from 'assert'
67 | *
68 | * const log = withLogger(IO.io)(C.log)
69 | *
70 | * const result = pipe(
71 | * IO.of(3),
72 | * log(n => `lifted "${n}" to the IO monad`), // n === 3
73 | * IO.map(n => n * n),
74 | * log(n => `squared the value, which is "${n}"`), // n === 9
75 | * )
76 | *
77 | * equal(result(), 9)
78 | */
79 | export function withLogger(
80 | M: MonadIO3
81 | ): (logger: LoggerIO) => (message: (a: A) => B) => (ma: Kind3) => Kind3
82 | export function withLogger(
83 | M: MonadIO2
84 | ): (logger: LoggerIO) => (message: (a: A) => B) => (ma: Kind2) => Kind2
85 | export function withLogger(
86 | M: MonadIO2C
87 | ): (logger: LoggerIO) => (message: (a: A) => B) => (ma: Kind2) => Kind2
88 | export function withLogger(
89 | M: MonadIO1
90 | ): (logger: LoggerIO) => (message: (a: A) => B) => (ma: Kind) => Kind
91 | export function withLogger(
92 | M: MonadIO
93 | ): (logger: LoggerIO) => (message: (a: A) => B) => (ma: HKT) => HKT
94 | export function withLogger(
95 | M: MonadIO
96 | ): (logger: LoggerIO) => (message: (a: A) => B) => (ma: HKT) => HKT {
97 | return (logger) => (message) => (ma) => M.chain(ma, (a) => M.map(M.fromIO(logger(message(a))), () => a))
98 | }
99 |
100 | const { contramap } = pipeable(loggerIO)
101 |
102 | export {
103 | /**
104 | * @since 0.3.0
105 | */
106 | contramap
107 | }
108 |
--------------------------------------------------------------------------------