Skip to content

Commit

Permalink
chore: add adonisjs example
Browse files Browse the repository at this point in the history
  • Loading branch information
Julien-R44 committed May 11, 2024
1 parent 29127db commit 2ea80a4
Show file tree
Hide file tree
Showing 20 changed files with 6,041 additions and 5 deletions.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,47 @@ Feel free to explore the different examples in the [examples](./examples) folder
- [cli.ts](./examples/cli.ts) - Example of using pino-loki as a CLI
- [custom_timestamp.ts](./examples/custom_timestamp.ts) - Example of using pino-loki with nanoseconds timestamps

## Usage in AdonisJS

Since AdonisJS use Pino as the default logger, you can use pino-loki easily by adding a new transport to the logger, in the `config/logger.ts` file:

```ts
import type { LokiOptions } from 'pino-loki'
import app from '@adonisjs/core/services/app'
import { defineConfig, targets } from '@adonisjs/core/logger'

import env from '#start/env'

const loggerConfig = defineConfig({
default: 'app',

loggers: {
app: {
enabled: true,
name: env.get('APP_NAME'),
level: env.get('LOG_LEVEL'),
transport: {
targets: targets()
.push({
target: 'pino-loki',
options: {
labels: { application: 'MY-APP' },
host: env.get('LOKI_HOST'),
basicAuth: {
username: env.get('LOKI_USERNAME'),
password: env.get('LOKI_PASSWORD'),
},
} satisfies LokiOptions,
})
.toArray(),
},
},
},
})
```

And you should be good to go! You can check the our [full example](./examples/adonisjs/) for more details.

# Limitations and considerations
## Out-of-order errors
Out-of-order Loki errors can occur due to the asynchronous nature of Pino. The fix to this is to allow for out-of-order logs in the Loki configuration. The reason why Loki doesn't have this enabled by default is because Promtail accounts for ordering constraints, however the same issue can also happen with promtail in high-load or when working with distributed networks.
Expand Down
10 changes: 5 additions & 5 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
networks:
loki:

services:
loki:
image: grafana/loki:2.8.0
ports:
- "3100:3100"
- '3100:3100'
command: -config.file=/etc/loki/local-config.yaml
networks:
- loki
Expand Down Expand Up @@ -36,6 +33,9 @@ services:
/run.sh
image: grafana/grafana:latest
ports:
- "3000:3000"
- '3000:3000'
networks:
- loki

networks:
loki:
25 changes: 25 additions & 0 deletions examples/adonisjs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Dependencies and AdonisJS build
node_modules
build
tmp

# Secrets
.env
.env.local
.env.production.local
.env.development.local

# Frontend assets compiled code
public/assets

# Build tools specific
npm-debug.log
yarn-error.log

# Editors specific
.fleet
.idea
.vscode

# Platform specific
.DS_Store
28 changes: 28 additions & 0 deletions examples/adonisjs/ace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
|--------------------------------------------------------------------------
| JavaScript entrypoint for running ace commands
|--------------------------------------------------------------------------
|
| DO NOT MODIFY THIS FILE AS IT WILL BE OVERRIDDEN DURING THE BUILD
| PROCESS.
|
| See docs.adonisjs.com/guides/typescript-build-process#creating-production-build
|
| Since, we cannot run TypeScript source code using "node" binary, we need
| a JavaScript entrypoint to run ace commands.
|
| This file registers the "ts-node/esm" hook with the Node.js module system
| and then imports the "bin/console.ts" file.
|
*/

/**
* Register hook to process TypeScript files using ts-node
*/
import { register } from 'node:module'
register('ts-node/esm', import.meta.url)

/**
* Import ace console entrypoint
*/
await import('./bin/console.js')
67 changes: 67 additions & 0 deletions examples/adonisjs/adonisrc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { defineConfig } from '@adonisjs/core/app'

export default defineConfig({
/*
|--------------------------------------------------------------------------
| Commands
|--------------------------------------------------------------------------
|
| List of ace commands to register from packages. The application commands
| will be scanned automatically from the "./commands" directory.
|
*/
commands: [() => import('@adonisjs/core/commands')],

/*
|--------------------------------------------------------------------------
| Service providers
|--------------------------------------------------------------------------
|
| List of service providers to import and register when booting the
| application
|
*/
providers: [
() => import('@adonisjs/core/providers/app_provider'),
() => import('@adonisjs/core/providers/hash_provider'),
{
file: () => import('@adonisjs/core/providers/repl_provider'),
environment: ['repl', 'test'],
},
],

/*
|--------------------------------------------------------------------------
| Preloads
|--------------------------------------------------------------------------
|
| List of modules to import before starting the application.
|
*/
preloads: [() => import('#start/routes'), () => import('#start/kernel')],

/*
|--------------------------------------------------------------------------
| Tests
|--------------------------------------------------------------------------
|
| List of test suites to organize tests by their type. Feel free to remove
| and add additional suites.
|
*/
tests: {
suites: [
{
files: ['tests/unit/**/*.spec(.ts|.js)'],
name: 'unit',
timeout: 2000,
},
{
files: ['tests/functional/**/*.spec(.ts|.js)'],
name: 'functional',
timeout: 30000,
},
],
forceExit: false,
},
})
35 changes: 35 additions & 0 deletions examples/adonisjs/app/exceptions/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import app from '@adonisjs/core/services/app'
import { HttpContext, ExceptionHandler } from '@adonisjs/core/http'

export default class HttpExceptionHandler extends ExceptionHandler {
/**
* In debug mode, the exception handler will display verbose errors
* with pretty printed stack traces.
*/
protected debug = !app.inProduction

/**
* Status pages are used to display a custom HTML pages for certain error
* codes. You might want to enable them in production only, but feel
* free to enable them in development as well.
*/
protected renderStatusPages = app.inProduction

/**
* The method is used for handling errors and returning
* response to the client
*/
async handle(error: unknown, ctx: HttpContext) {
return super.handle(error, ctx)
}

/**
* The method is used to report error to the logging service or
* the a third party error monitoring service.
*
* @note You should not attempt to send a response from this method.
*/
async report(error: unknown, ctx: HttpContext) {
return super.report(error, ctx)
}
}
19 changes: 19 additions & 0 deletions examples/adonisjs/app/middleware/container_bindings_middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Logger } from '@adonisjs/core/logger'
import { HttpContext } from '@adonisjs/core/http'
import { NextFn } from '@adonisjs/core/types/http'

/**
* The container bindings middleware binds classes to their request
* specific value using the container resolver.
*
* - We bind "HttpContext" class to the "ctx" object
* - And bind "Logger" class to the "ctx.logger" object
*/
export default class ContainerBindingsMiddleware {
handle(ctx: HttpContext, next: NextFn) {
ctx.containerResolver.bindValue(HttpContext, ctx)
ctx.containerResolver.bindValue(Logger, ctx.logger)

return next()
}
}
47 changes: 47 additions & 0 deletions examples/adonisjs/bin/console.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
|--------------------------------------------------------------------------
| Ace entry point
|--------------------------------------------------------------------------
|
| The "console.ts" file is the entrypoint for booting the AdonisJS
| command-line framework and executing commands.
|
| Commands do not boot the application, unless the currently running command
| has "options.startApp" flag set to true.
|
*/

import 'reflect-metadata'
import { Ignitor, prettyPrintError } from '@adonisjs/core'

/**
* URL to the application root. AdonisJS need it to resolve
* paths to file and directories for scaffolding commands
*/
const APP_ROOT = new URL('../', import.meta.url)

/**
* The importer is used to import files in context of the
* application.
*/
const IMPORTER = (filePath: string) => {
if (filePath.startsWith('./') || filePath.startsWith('../')) {
return import(new URL(filePath, APP_ROOT).href)
}
return import(filePath)
}

new Ignitor(APP_ROOT, { importer: IMPORTER })
.tap((app) => {
app.booting(async () => {
await import('#start/env')
})
app.listen('SIGTERM', () => app.terminate())
app.listenIf(app.managedByPm2, 'SIGINT', () => app.terminate())
})
.ace()
.handle(process.argv.splice(2))
.catch((error) => {
process.exitCode = 1
prettyPrintError(error)
})
45 changes: 45 additions & 0 deletions examples/adonisjs/bin/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
|--------------------------------------------------------------------------
| HTTP server entrypoint
|--------------------------------------------------------------------------
|
| The "server.ts" file is the entrypoint for starting the AdonisJS HTTP
| server. Either you can run this file directly or use the "serve"
| command to run this file and monitor file changes
|
*/

import 'reflect-metadata'
import { Ignitor, prettyPrintError } from '@adonisjs/core'

/**
* URL to the application root. AdonisJS need it to resolve
* paths to file and directories for scaffolding commands
*/
const APP_ROOT = new URL('../', import.meta.url)

/**
* The importer is used to import files in context of the
* application.
*/
const IMPORTER = (filePath: string) => {
if (filePath.startsWith('./') || filePath.startsWith('../')) {
return import(new URL(filePath, APP_ROOT).href)
}
return import(filePath)
}

new Ignitor(APP_ROOT, { importer: IMPORTER })
.tap((app) => {
app.booting(async () => {
await import('#start/env')
})
app.listen('SIGTERM', () => app.terminate())
app.listenIf(app.managedByPm2, 'SIGINT', () => app.terminate())
})
.httpServer()
.start()
.catch((error) => {
process.exitCode = 1
prettyPrintError(error)
})
Loading

0 comments on commit 2ea80a4

Please sign in to comment.