Skip to content

Commit 1dc5fc7

Browse files
committed
Merge branch 'cacheable---fix-updating-types-in-tests' of https://github.com/jaredwray/cacheable into cacheable---fix-updating-types-in-tests
2 parents a3ab04e + 744eb81 commit 1dc5fc7

File tree

5 files changed

+404
-3
lines changed

5 files changed

+404
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ packages/website/site/docs/*.md
2424
packages/cacheable-request/test/testdb.sqlite
2525
packages/flat-cache/.cache
2626
packages/file-entry-cache/.cache
27+
packages/file-entry-cache/test/test-logger-file.txt

packages/file-entry-cache/README.md

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@
3636
- [Path Security and Traversal Prevention](#path-security-and-traversal-prevention)
3737
- [Using Checksums to Determine if a File has Changed (useCheckSum)](#using-checksums-to-determine-if-a-file-has-changed-usechecksum)
3838
- [Setting Additional Meta Data](#setting-additional-meta-data)
39+
- [Logger Support](#logger-support)
40+
- [Logger Interface](#logger-interface)
41+
- [Using Pino Logger](#using-pino-logger)
42+
- [Log Levels](#log-levels)
43+
- [Example with Custom Log Levels](#example-with-custom-log-levels)
44+
- [Debugging Cache Operations](#debugging-cache-operations)
3945
- [How to Contribute](#how-to-contribute)
4046
- [License and Copyright](#license-and-copyright)
4147

@@ -117,6 +123,7 @@ There have been many features added and changes made to the `file-entry-cache` c
117123
- `hashAlgorithm?` - The algorithm to use for the checksum. Default is `md5` but can be any algorithm supported by `crypto.createHash`
118124
- `cwd?` - The current working directory for resolving relative paths. Default is `process.cwd()`
119125
- `strictPaths?` - If `true` restricts file access to within `cwd` boundaries, preventing path traversal attacks. Default is `true`
126+
- `logger?` - A logger instance compatible with Pino logger interface for debugging and monitoring. Default is `undefined`
120127
- `cache.ttl?` - The time to live for the cache in milliseconds. Default is `0` which means no expiration
121128
- `cache.lruSize?` - The number of items to keep in the cache. Default is `0` which means no limit
122129
- `cache.useClone?` - If `true` it will clone the data before returning it. Default is `false`
@@ -135,6 +142,7 @@ There have been many features added and changes made to the `file-entry-cache` c
135142
- `getHash(buffer: Buffer): string` - Gets the hash of a buffer used for checksums
136143
- `cwd: string` - The current working directory for resolving relative paths. Default is `process.cwd()`
137144
- `strictPaths: boolean` - If `true` restricts file access to within `cwd` boundaries. Default is `true`
145+
- `logger: ILogger | undefined` - A logger instance for debugging and monitoring cache operations
138146
- `createFileKey(filePath: string): string` - Returns the cache key for the file path (returns the path exactly as provided).
139147
- `deleteCacheFile(): boolean` - Deletes the cache file from disk
140148
- `destroy(): void` - Destroys the cache. This will clear the cache in memory. If using cache persistence it will stop the interval.
@@ -396,13 +404,132 @@ const fileDescriptor = fileEntryCache.getFileDescriptor('file.txt', { useCheckSu
396404

397405
# Setting Additional Meta Data
398406

399-
In the past we have seen people do random values on the `meta` object. This can cause issues with the `meta` object. To avoid this we have `data` which can be anything.
407+
In the past we have seen people do random values on the `meta` object. This can cause issues with the `meta` object. To avoid this we have `data` which can be anything.
400408

401-
```javascript
409+
```javascript
402410
const fileEntryCache = new FileEntryCache();
403411
const fileDescriptor = fileEntryCache.getFileDescriptor('file.txt');
404412
fileDescriptor.meta.data = { myData: 'myData' }; //anything you want
405413
```
414+
415+
# Logger Support
416+
417+
The `FileEntryCache` supports logging through a Pino-compatible logger interface. This is useful for debugging and monitoring cache operations in production environments.
418+
419+
## Logger Interface
420+
421+
The logger must implement the following interface:
422+
423+
```typescript
424+
interface ILogger {
425+
level?: string;
426+
trace: (message: string | object, ...args: unknown[]) => void;
427+
debug: (message: string | object, ...args: unknown[]) => void;
428+
info: (message: string | object, ...args: unknown[]) => void;
429+
warn: (message: string | object, ...args: unknown[]) => void;
430+
error: (message: string | object, ...args: unknown[]) => void;
431+
fatal: (message: string | object, ...args: unknown[]) => void;
432+
}
433+
```
434+
435+
## Using Pino Logger
436+
437+
You can pass a Pino logger instance to the `FileEntryCache` constructor or set it via the `logger` property:
438+
439+
```javascript
440+
import pino from 'pino';
441+
import fileEntryCache from 'file-entry-cache';
442+
443+
// Create a Pino logger
444+
const logger = pino({
445+
level: 'debug',
446+
transport: {
447+
target: 'pino-pretty',
448+
options: {
449+
colorize: true
450+
}
451+
}
452+
});
453+
454+
// Pass logger in constructor
455+
const cache = new fileEntryCache.FileEntryCache({
456+
logger,
457+
cacheId: 'my-cache'
458+
});
459+
460+
// Or set it after creation
461+
cache.logger = logger;
462+
463+
// Now all cache operations will be logged
464+
const descriptor = cache.getFileDescriptor('./src/file.txt');
465+
```
466+
467+
## Log Levels
468+
469+
The logger will output different levels of information:
470+
471+
- **trace**: Detailed internal operations (key creation, cached meta lookup, file stats)
472+
- **debug**: Method entry, checksum settings, change detection, file status
473+
- **info**: Important state changes (file has changed)
474+
- **error**: File read errors and exceptions
475+
476+
## Example with Custom Log Levels
477+
478+
```javascript
479+
import pino from 'pino';
480+
import { FileEntryCache } from 'file-entry-cache';
481+
482+
// Create logger with specific level
483+
const logger = pino({ level: 'info' });
484+
485+
const cache = new FileEntryCache({
486+
logger,
487+
useCheckSum: true
488+
});
489+
490+
// This will log at info level when files change
491+
const files = ['./src/index.js', './src/utils.js'];
492+
files.forEach(file => {
493+
const descriptor = cache.getFileDescriptor(file);
494+
if (descriptor.changed) {
495+
console.log(`Processing changed file: ${file}`);
496+
}
497+
});
498+
499+
cache.reconcile();
500+
```
501+
502+
## Debugging Cache Operations
503+
504+
For detailed debugging, set the logger level to `debug` or `trace`:
505+
506+
```javascript
507+
import pino from 'pino';
508+
import { FileEntryCache } from 'file-entry-cache';
509+
510+
const logger = pino({
511+
level: 'trace',
512+
transport: {
513+
target: 'pino-pretty'
514+
}
515+
});
516+
517+
const cache = new FileEntryCache({
518+
logger,
519+
useCheckSum: true,
520+
cwd: '/project/root'
521+
});
522+
523+
// Will log detailed information about:
524+
// - File path resolution
525+
// - Cache key creation
526+
// - Cached metadata lookup
527+
// - File stats reading
528+
// - Hash calculation (if using checksums)
529+
// - Change detection logic
530+
const descriptor = cache.getFileDescriptor('./src/app.js');
531+
```
532+
406533
# How to Contribute
407534

408535
You can contribute by forking the repo and submitting a pull request. Please make sure to add tests and update the documentation. To learn more about how to contribute go to our main README [https://github.com/jaredwray/cacheable](https://github.com/jaredwray/cacheable). This will talk about how to `Open a Pull Request`, `Ask a Question`, or `Post an Issue`.

packages/file-entry-cache/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"@biomejs/biome": "^2.2.5",
4141
"@types/node": "^24.7.0",
4242
"@vitest/coverage-v8": "^3.2.4",
43+
"pino": "^10.0.0",
4344
"rimraf": "^6.0.1",
4445
"tsup": "^8.5.0",
4546
"typescript": "^5.9.3",

packages/file-entry-cache/src/index.ts

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,23 @@ import {
88
type FlatCacheOptions,
99
} from "flat-cache";
1010

11+
export type ILogger = {
12+
/** Current log level */
13+
level?: string;
14+
/** Trace level logging */
15+
trace: (message: string | object, ...args: unknown[]) => void;
16+
/** Debug level logging */
17+
debug: (message: string | object, ...args: unknown[]) => void;
18+
/** Info level logging */
19+
info: (message: string | object, ...args: unknown[]) => void;
20+
/** Warning level logging */
21+
warn: (message: string | object, ...args: unknown[]) => void;
22+
/** Error level logging */
23+
error: (message: string | object, ...args: unknown[]) => void;
24+
/** Fatal level logging */
25+
fatal: (message: string | object, ...args: unknown[]) => void;
26+
};
27+
1128
export type FileEntryCacheOptions = {
1229
/** Whether to use file modified time for change detection (default: true) */
1330
useModifiedTime?: boolean;
@@ -19,6 +36,8 @@ export type FileEntryCacheOptions = {
1936
cwd?: string;
2037
/** Restrict file access to within cwd boundaries (default: true) */
2138
strictPaths?: boolean;
39+
/** Logger instance for logging (default: undefined) */
40+
logger?: ILogger;
2241
/** Options for the underlying flat cache */
2342
cache?: FlatCacheOptions;
2443
};
@@ -127,6 +146,7 @@ export class FileEntryCache {
127146
private _hashAlgorithm = "md5";
128147
private _cwd: string = process.cwd();
129148
private _strictPaths = true;
149+
private _logger?: ILogger;
130150

131151
/**
132152
* Create a new FileEntryCache instance
@@ -152,6 +172,10 @@ export class FileEntryCache {
152172
if (options?.strictPaths !== undefined) {
153173
this._strictPaths = options.strictPaths;
154174
}
175+
176+
if (options?.logger) {
177+
this._logger = options.logger;
178+
}
155179
}
156180

157181
/**
@@ -170,6 +194,22 @@ export class FileEntryCache {
170194
this._cache = cache;
171195
}
172196

197+
/**
198+
* Get the logger
199+
* @returns {ILogger | undefined} The logger instance
200+
*/
201+
public get logger(): ILogger | undefined {
202+
return this._logger;
203+
}
204+
205+
/**
206+
* Set the logger
207+
* @param {ILogger | undefined} logger - The logger to set
208+
*/
209+
public set logger(logger: ILogger | undefined) {
210+
this._logger = logger;
211+
}
212+
173213
/**
174214
* Use the hash to check if the file has changed
175215
* @returns {boolean} if the hash is used to check if the file has changed (default: false)
@@ -337,39 +377,62 @@ export class FileEntryCache {
337377
filePath: string,
338378
options?: GetFileDescriptorOptions,
339379
): FileDescriptor {
380+
this._logger?.debug({ filePath, options }, "Getting file descriptor");
381+
340382
let fstat: fs.Stats;
341383
const result: FileDescriptor = {
342384
key: this.createFileKey(filePath),
343385
changed: false,
344386
meta: {},
345387
};
346388

389+
this._logger?.trace({ key: result.key }, "Created file key");
390+
347391
const metaCache = this._cache.getKey<FileDescriptorMeta>(result.key);
348392

393+
if (metaCache) {
394+
this._logger?.trace({ metaCache }, "Found cached meta");
395+
} else {
396+
this._logger?.trace("No cached meta found");
397+
}
398+
349399
// Start with cached meta to preserve custom properties
350400
result.meta = metaCache ? { ...metaCache } : {};
351401

352402
// Convert to absolute path for file system operations
353403
const absolutePath = this.getAbsolutePath(filePath);
404+
this._logger?.trace({ absolutePath }, "Resolved absolute path");
354405

355406
const useCheckSumValue = options?.useCheckSum ?? this._useCheckSum;
407+
this._logger?.debug(
408+
{ useCheckSum: useCheckSumValue },
409+
"Using checksum setting",
410+
);
356411

357412
try {
358413
fstat = fs.statSync(absolutePath);
359414
// Update the file stats while preserving existing meta properties
360415
result.meta.size = fstat.size;
361416
result.meta.mtime = fstat.mtime.getTime();
362417

418+
this._logger?.trace(
419+
{ size: result.meta.size, mtime: result.meta.mtime },
420+
"Read file stats",
421+
);
422+
363423
if (useCheckSumValue) {
364424
// Get the file hash
365425
const buffer = fs.readFileSync(absolutePath);
366426
result.meta.hash = this.getHash(buffer);
427+
this._logger?.trace({ hash: result.meta.hash }, "Calculated file hash");
367428
}
368429
} catch (error) {
430+
this._logger?.error({ filePath, error }, "Error reading file");
369431
this.removeEntry(filePath);
370432
let notFound = false;
371433
if ((error as Error).message.includes("ENOENT")) {
372434
notFound = true;
435+
this._logger?.debug({ filePath }, "File not found");
373436
}
374437

375438
return {
@@ -384,25 +447,43 @@ export class FileEntryCache {
384447
if (!metaCache) {
385448
result.changed = true;
386449
this._cache.setKey(result.key, result.meta);
450+
this._logger?.debug({ filePath }, "File not in cache, marked as changed");
387451
return result;
388452
}
389453

390454
// If the file is in the cache, check if the file has changed
391-
/* c8 ignore next 3 */
392455
if (useCheckSumValue === false && metaCache?.mtime !== result.meta?.mtime) {
393456
result.changed = true;
457+
this._logger?.debug(
458+
{ filePath, oldMtime: metaCache.mtime, newMtime: result.meta.mtime },
459+
"File changed: mtime differs",
460+
);
394461
}
395462

396463
if (metaCache?.size !== result.meta?.size) {
397464
result.changed = true;
465+
this._logger?.debug(
466+
{ filePath, oldSize: metaCache.size, newSize: result.meta.size },
467+
"File changed: size differs",
468+
);
398469
}
399470

400471
if (useCheckSumValue && metaCache?.hash !== result.meta?.hash) {
401472
result.changed = true;
473+
this._logger?.debug(
474+
{ filePath, oldHash: metaCache.hash, newHash: result.meta.hash },
475+
"File changed: hash differs",
476+
);
402477
}
403478

404479
this._cache.setKey(result.key, result.meta);
405480

481+
if (result.changed) {
482+
this._logger?.info({ filePath }, "File has changed");
483+
} else {
484+
this._logger?.debug({ filePath }, "File unchanged");
485+
}
486+
406487
return result;
407488
}
408489

0 commit comments

Comments
 (0)