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
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
// 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:
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:
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: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: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");
});
// 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: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");
});
// 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
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: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);
});
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)
// 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: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
const logs = Naite.get("user:*").result();
// [{ ... }, { ... }, { ... }]
first() - First
const first = Naite.get("user:create").first();
// { userId: 123, username: "john" }
// undefined if not found
const empty = Naite.get("nonexistent").first();
// undefined
last() - Last
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
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
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
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
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
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
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
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
// ✅ 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
// ✅ 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
// ✅ 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(): 1. Empty results: Returns empty array if no logs 2.
Order: Returns in recorded order 3. Chaining: Each method returns a new NaiteQuery 4.
Wildcards: Understand pattern matching rules 5. where path: Uses radashi’s get path format
Next Steps
Naite Viewer
VSCode Extension usage
Debugging Tests
Using callstack tracking
Recording Logs
Detailed Naite.t() usage
What is Naite?
Naite overview