Skip to content

Commit fe7e861

Browse files
committed
feat: Add ServiceConfig.crashesLength property, default to 0
1 parent 5788792 commit fe7e861

File tree

8 files changed

+47
-22
lines changed

8 files changed

+47
-22
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ startCompositeService({
168168
// Do "something async" before restarting the service
169169
await doSomethingAsync()
170170
},
171+
// Set max length of `ctx.crash.logTail` used above (default is 0)
172+
logTailLength: 5,
171173
},
172174
})
173175
```

TODO.md

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,7 @@
11
# TODO
22

3-
remove minimumRestartDelay
4-
default logTailLength = 5
5-
default crashesLength = 3
6-
73
simplify test utils by using `stream-line-reader` packge
8-
ugly output for crashing from error in onCrash/ready
94

10-
- bugs
11-
- without windowsCtrlCShutdown=true, composed service programs like `nodemon` (which have their own child process) cannot be stopped on Windows
12-
- make note that windowsCtrlCShutdown may be needed if your service has it's own subprocesses
13-
- docs
14-
- http gateway
15-
- warnings about using http gateway for production:
16-
"http gateway is useful for getting up & running quickly,
17-
and does a pretty good job performing it's function,
18-
but may not be suitable for serious production deployments.
19-
Check out https://github.com/awesome-selfhosted/awesome-selfhosted#proxy"
20-
- https://v2.docusaurus.io/docs/markdown-features#line-highlighting
21-
- more examples in guide on configuring http-proxy-middleware
225
- community
236
- https://github.com/zenflow/composite-service/community
247
- PR to add composite-service example to https://docs.docker.com/config/containers/multi-service_container/

src/Service.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,12 @@ export class Service {
9292
date: new Date(),
9393
logTail: proc.logTail,
9494
}
95-
this.crashes.push(crash)
95+
if (this.config.crashesLength > 0) {
96+
this.crashes.push(crash)
97+
if (this.crashes.length > this.config.crashesLength) {
98+
this.crashes.shift()
99+
}
100+
}
96101
const isServiceReady = await isResolved(this.ready!)
97102
const ctx: OnCrashContext = {
98103
serviceId: this.id,

src/interfaces/OnCrashContext.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ export interface OnCrashContext {
2020
crash: ServiceCrash
2121

2222
/**
23-
* Array of objects representing all crashes, past and present
23+
* Objects representing latest crashes, ordered from oldest to newest
24+
*
25+
* Maximum length is determined by {@link ServiceConfig.crashesLength}.
2426
*/
2527
crashes: ServiceCrash[]
2628
}

src/interfaces/ServiceConfig.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,19 @@ export interface ServiceConfig {
8282
onCrash?: (ctx: OnCrashContext) => void | Promise<void>
8383

8484
/**
85-
* Maximum number of lines to keep from the tail of each child process's log output.
85+
* Maximum number of latest crashes to keep record of.
8686
* Defaults to `0`.
8787
*
88-
* The log lines for each crashed process can be accessed in your {@link ServiceConfig.onCrash} function,
88+
* The recorded crashes can be accessed in your {@link ServiceConfig.onCrash} function,
89+
* as `ctx.crashes`.
90+
*/
91+
crashesLength?: number
92+
93+
/**
94+
* Maximum number of lines to keep from the tail of the child process's console output.
95+
* Defaults to `0`.
96+
*
97+
* The log lines for can be accessed in your {@link ServiceConfig.onCrash} function,
8998
* as `ctx.crash.logTail` or `ctx.crashes[i].logTail`.
9099
*/
91100
logTailLength?: number

src/interfaces/ServiceCrash.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export interface ServiceCrash {
77
*/
88
date: Date
99
/**
10-
* Tail of the service's log output
10+
* Tail of the process's log output
1111
*
1212
* Maximum length is determined by {@link ServiceConfig.logTailLength}.
1313
*/

src/validateAndNormalizeConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface NormalizedServiceConfig {
2121
ready: (ctx: ReadyContext) => Promise<void>
2222
forceKillTimeout: number
2323
onCrash: (ctx: OnCrashContext) => void
24+
crashesLength: number
2425
logTailLength: number
2526
minimumRestartDelay: number
2627
}
@@ -87,6 +88,7 @@ function processServiceConfig(
8788
onCrash: (ctx: OnCrashContext) => {
8889
if (!ctx.isServiceReady) throw new Error('Crashed before becoming ready')
8990
},
91+
crashesLength: 0,
9092
logTailLength: 0,
9193
minimumRestartDelay: 0,
9294
...removeUndefinedProperties(defaults),

test/integration/crashing.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ describe('crashing', () => {
184184
it('restarts service on successful pre-ready onCrash', async () => {
185185
const script = getScript(`
186186
config.services.second.command = ['node', '-e', 'console.log("Crashing")'];
187+
config.services.second.crashesLength = 3;
187188
config.services.second.onCrash = ctx => {
188189
if (ctx.crashes.length === 3) throw new Error('Crashed three times');
189190
};
@@ -220,6 +221,7 @@ describe('crashing', () => {
220221
})
221222
it('restarts service on successful post-ready onCrash', async () => {
222223
const script = getScript(`
224+
config.services.second.crashesLength = 2;
223225
config.services.second.logTailLength = 1;
224226
config.services.second.onCrash = async ctx => {
225227
const tests = [
@@ -282,5 +284,25 @@ describe('crashing', () => {
282284
"second | Started 🚀",
283285
]
284286
`)
287+
288+
// crash again
289+
expect(await fetchText('http://localhost:8002/?crash')).toBe('crashing')
290+
// allow time for restart again
291+
await delay(500)
292+
// make sure it restarted again
293+
expect(await fetchText('http://localhost:8002/')).toBe('second')
294+
// correct output for 3rd crash
295+
expect(proc.flushOutput()).toMatchInlineSnapshot(`
296+
Array [
297+
"second | Crashing",
298+
" (info) Service 'second' crashed",
299+
"number of crashes: 2",
300+
"crash logTail: [\\"Crashing\\\\n\\"]",
301+
"Handling crash...",
302+
"Done handling crash",
303+
" (info) Restarting service 'second'",
304+
"second | Started 🚀",
305+
]
306+
`)
285307
})
286308
})

0 commit comments

Comments
 (0)