Naite.get() Overview
Wildcard Patterns
Flexible key matchingHierarchical queries
Chaining Queries
fromFile, fromFunctionwhere conditions
Various Results
result, first, lastat(index)
Callstack Usage
Call location trackingDebugging support
Basic Usage
Query by Key
Copy
import { Naite } from "sonamu";
// Record log
Naite.t("user:create", { userId: 123, username: "john" });
// Query by exact key
const data = Naite.get("user:create").result();
// [{ userId: 123, username: "john" }]
// First data
const first = Naite.get("user:create").first();
// { userId: 123, username: "john" }
Wildcard Patterns
Copy
// Record multiple logs
Naite.t("user:create", { action: "create" });
Naite.t("user:update", { action: "update" });
Naite.t("user:delete", { action: "delete" });
Naite.t("post:create", { action: "create" });
// user:* pattern
const userLogs = Naite.get("user:*").result();
// Matches user:create, user:update, user:delete
// [{ action: "create" }, { action: "update" }, { action: "delete" }]
// *:create pattern
const createLogs = Naite.get("*:create").result();
// Matches user:create, post:create
Wildcard Pattern Rules
1. Prefix Matching
When ending with*, prefix matching:
Copy
Naite.t("syncer:renderTemplate", { /* ... */ });
Naite.t("syncer:renderTemplate:user", { /* ... */ });
Naite.t("syncer:writeFile", { /* ... */ });
// "syncer:*" β All keys starting with syncer: regardless of length
const logs = Naite.get("syncer:*").result();
// syncer:renderTemplate
// syncer:renderTemplate:user
// syncer:writeFile
// All matched
2. Middle Wildcard
When* is in the middle:
Copy
Naite.t("syncer:renderTemplate:user", { /* ... */ });
Naite.t("syncer:writeFile:user", { /* ... */ });
Naite.t("syncer:renderTemplate:post", { /* ... */ });
// "syncer:*:user" β Middle part is * (same length)
const logs = Naite.get("syncer:*:user").result();
// syncer:renderTemplate:user
// syncer:writeFile:user
// Matched
// syncer:renderTemplate:post not matched (last is not user)
3. Exact Matching
Without wildcard, only exact key matches:Copy
Naite.t("user:create", { /* ... */ });
Naite.t("user:create:validation", { /* ... */ });
// "user:create" β Exact match only
const logs = Naite.get("user:create").result();
// Only user:create matched
// user:create:validation not matched
Chaining Methods
fromFile() - Filter by Filename
Query only logs called from a specific file:Copy
test("file filtering", async () => {
// Logs recorded from multiple files
// From user.model.test.ts:
Naite.t("test:log", { file: "user.model.test.ts" });
// From post.model.test.ts:
Naite.t("test:log", { file: "post.model.test.ts" });
// Query only logs from user.model.test.ts
const userLogs = Naite.get("test:log")
.fromFile("user.model.test.ts")
.result();
expect(userLogs).toHaveLength(1);
expect(userLogs[0].file).toBe("user.model.test.ts");
});
Copy
// Checks if callstack's filePath ends with /.../{fileName}
fromFile(fileName: string): NaiteQuery {
const filtered = this.traces.filter((t) =>
t.stack.some((frame) => frame.filePath.endsWith(`/${fileName}`))
);
return new NaiteQuery(filtered);
}
fromFunction() - Filter by Function Name
Query only logs called from a specific function:Copy
async function renderTemplate(template: string) {
Naite.t("syncer:render", { template });
// ...
}
async function writeFile(path: string) {
Naite.t("syncer:write", { path });
// ...
}
test("function filtering", async () => {
await renderTemplate("user.model.ts");
await writeFile("output.ts");
// Only logs called from renderTemplate
const renderLogs = Naite.get("syncer:*")
.fromFunction("renderTemplate")
.result();
expect(renderLogs).toHaveLength(1);
expect(renderLogs[0].template).toBe("user.model.ts");
});
Copy
// Direct calls only (stack[0])
fromFunction("renderTemplate", { from: "direct" })
// Indirect calls only (stack[1+])
fromFunction("renderTemplate", { from: "indirect" })
// Both (default)
fromFunction("renderTemplate", { from: "both" })
fromFunction("renderTemplate") // Same
Copy
async function processUser(userId: number) {
Naite.t("user:process:start", { userId });
await createUser(userId);
Naite.t("user:process:done", { userId });
}
async function createUser(userId: number) {
Naite.t("user:create", { userId });
}
test("call relationship tracking", async () => {
await processUser(123);
// Only logs directly called from processUser
const directLogs = Naite.get("user:*")
.fromFunction("processUser", { from: "direct" })
.result();
// user:process:start, user:process:done
expect(directLogs).toHaveLength(2);
// All logs in processUser's call chain
const allLogs = Naite.get("user:*")
.fromFunction("processUser", { from: "both" })
.result();
// user:process:start, user:create, user:process:done
expect(allLogs).toHaveLength(3);
});
where() - Filter by Data Conditions
Filter by specific fields in log data:Copy
test("condition filtering", async () => {
Naite.t("user:create", { userId: 1, status: "active" });
Naite.t("user:create", { userId: 2, status: "inactive" });
Naite.t("user:create", { userId: 3, status: "active" });
// userId > 1
const logs = Naite.get("user:create")
.where("data.userId", ">", 1)
.result();
expect(logs).toHaveLength(2);
expect(logs[0].userId).toBe(2);
expect(logs[1].userId).toBe(3);
// Only where status is active
const activeLogs = Naite.get("user:create")
.where("data.status", "=", "active")
.result();
expect(activeLogs).toHaveLength(2);
});
Copy
where("data.userId", ">", 10) // Greater than
where("data.userId", "<", 100) // Less than
where("data.userId", ">=", 10) // Greater than or equal
where("data.userId", "<=", 100) // Less than or equal
where("data.status", "=", "active") // Equal
where("data.status", "!=", "deleted") // Not equal
where("data.username", "includes", "john") // Contains (string)
Copy
// Access data field
where("data.userId", "=", 123)
// Access nested objects (uses radashi's get)
where("data.user.profile.age", ">", 18)
Chaining Combinations
Write complex queries by chaining multiple methods:Copy
test("complex query", async () => {
// Chain multiple conditions
const logs = Naite.get("user:*")
.fromFile("user.model.test.ts")
.fromFunction("createUser")
.where("data.userId", ">", 100)
.where("data.status", "=", "active")
.result();
// From user.model.test.ts file
// Called by createUser function
// userId greater than 100
// status is active
});
Result Query Methods
result() - Full Array
Copy
const logs = Naite.get("user:*").result();
// [{ ... }, { ... }, { ... }]
first() - First
Copy
const first = Naite.get("user:create").first();
// { userId: 123, username: "john" }
// undefined if not found
const empty = Naite.get("nonexistent").first();
// undefined
last() - Last
Copy
Naite.t("user:create", { userId: 1 });
Naite.t("user:create", { userId: 2 });
Naite.t("user:create", { userId: 3 });
const last = Naite.get("user:create").last();
// { userId: 3 }
at(index) - nth
Copy
Naite.t("user:create", { userId: 1 });
Naite.t("user:create", { userId: 2 });
Naite.t("user:create", { userId: 3 });
const second = Naite.get("user:create").at(1);
// { userId: 2 }
// undefined if out of range
const outOfRange = Naite.get("user:create").at(10);
// undefined
Practical Examples
1. Complex Flow Verification
Copy
test("post creation flow", async () => {
const postModel = new PostModel();
const { post } = await postModel.create({
title: "Test Post",
content: "Content",
author_id: 1,
});
// Verify entire flow
const allLogs = Naite.get("post:create:*").result();
expect(allLogs.length).toBeGreaterThan(0);
// Verify input data
const input = Naite.get("post:create:input").first();
expect(input.title).toBe("Test Post");
// Verify output data
const output = Naite.get("post:create:output").first();
expect(output.postId).toBeGreaterThan(0);
// Verify DB query
const dbLogs = Naite.get("post:create:*")
.fromFunction("insertInto")
.result();
expect(dbLogs.length).toBeGreaterThan(0);
});
2. Syncer Behavior Verification
Copy
test("Syncer template rendering", async () => {
await Sonamu.syncer.generateTemplate("model", {
entityId: "User"
});
// Verify renderTemplate function call
const renderLogs = Naite.get("syncer:*")
.fromFunction("renderTemplate")
.result();
expect(renderLogs.length).toBeGreaterThan(0);
// Verify User entity processing
const userLogs = Naite.get("syncer:*")
.where("data.entityId", "=", "User")
.result();
expect(userLogs.length).toBeGreaterThan(0);
});
3. Error Occurrence Tracking
Copy
test("error situation tracking", async () => {
try {
await createUser({ username: "" }); // Invalid input
} catch (error) {
// Check error logs
const errorLogs = Naite.get("user:create:error").result();
expect(errorLogs.length).toBeGreaterThan(0);
// Check error cause
const validationErrors = Naite.get("user:*")
.where("data.error", "includes", "validation")
.result();
expect(validationErrors.length).toBeGreaterThan(0);
}
});
4. Performance Measurement
Copy
test("processing time measurement", async () => {
Naite.t("process:start", { at: new Date() });
// Long running task
await longRunningTask();
Naite.t("process:end", { at: new Date() });
const logs = Naite.get("process:*").result();
const start = new Date(logs[0].at);
const end = new Date(logs[1].at);
const duration = end.getTime() - start.getTime();
expect(duration).toBeLessThan(1000); // Within 1 second
});
Internal Structure
NaiteQuery Class
Copy
export class NaiteQuery {
constructor(private traces: NaiteTrace[]) {}
fromFile(fileName: string): NaiteQuery {
// Filter and return new NaiteQuery (chainable)
}
fromFunction(funcName: string, options?): NaiteQuery {
// Filter and return new NaiteQuery
}
where(path: string, operator: string, value: any): NaiteQuery {
// Filter and return new NaiteQuery
}
result(): any[] {
// Return final data array
}
first(): any | undefined {
// First data
}
last(): any | undefined {
// Last data
}
at(index: number): any | undefined {
// nth data
}
}
matchesPattern Logic
Copy
function matchesPattern(key: string, pattern: string): boolean {
const keyParts = key.split(":");
const patternParts = pattern.split(":");
// Last is * β prefix matching (any length)
if (patternParts[patternParts.length - 1] === "*") {
const prefixParts = patternParts.slice(0, -1);
return prefixParts.every((part, i) => part === keyParts[i]);
}
// Must be same length
if (patternParts.length !== keyParts.length) {
return false;
}
// Each part is * or exact match
return patternParts.every((part, i) =>
part === "*" || part === keyParts[i]
);
}
Best Practices
1. Clear Key Structure
Copy
// β
Correct way: Hierarchical structure
Naite.t("module:function:action", { /* ... */ });
// Use wildcards when querying
Naite.get("module:*") // Entire module
Naite.get("module:function:*") // Specific function
2. Optimize Chaining Order
Copy
// β
Good way: Order by most filtering first
Naite.get("user:*")
.fromFile("user.model.test.ts") // File filter (filters most)
.fromFunction("createUser") // Function filter
.where("data.userId", ">", 100) // Data filter
.result();
3. Result Verification
Copy
// β
Correct way: Verify expected results
const logs = Naite.get("user:create").result();
expect(logs.length).toBeGreaterThan(0); // Check not empty
// Verify specific values
expect(logs[0].userId).toBeGreaterThan(0);
expect(logs[0].username).toBe("john");
Cautions
Cautions when using Naite.get():
- Empty results: Returns empty array if no logs
- Order: Returns in recorded order
- Chaining: Each method returns a new NaiteQuery
- Wildcards: Understand pattern matching rules
- where path: Uses radashiβs get path format