| name | evernote-debug-bundle |
| description | Debug Evernote API issues with diagnostic tools and techniques.
Use when troubleshooting API calls, inspecting requests/responses,
or diagnosing integration problems.
Trigger with phrases like "debug evernote", "evernote diagnostic",
"troubleshoot evernote", "evernote logs", "inspect evernote".
|
| allowed-tools | Read, Write, Edit, Bash(npm:*), Bash(node:*), Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Evernote Debug Bundle
Overview
Comprehensive debugging toolkit for Evernote API integrations, including request logging, ENML validation, token inspection, and diagnostic utilities.
Prerequisites
- Evernote SDK installed
- Node.js environment
- Understanding of common Evernote errors
Instructions
Step 1: Debug Logger
const fs = require('fs');
const path = require('path');
class EvernoteDebugLogger {
constructor(options = {}) {
this.enabled = options.enabled ?? process.env.EVERNOTE_DEBUG === 'true';
this.logFile = options.logFile || 'evernote-debug.log';
this.logDir = options.logDir || './logs';
this.maxLogSize = options.maxLogSize || 10 * 1024 * 1024;
if (this.enabled) {
fs.mkdirSync(this.logDir, { recursive: true });
}
}
log(operation, data) {
if (!this.enabled) return;
const entry = {
timestamp: new Date().toISOString(),
operation,
...data
};
console.log(`[EVERNOTE DEBUG] ${operation}`, JSON.stringify(data, null, 2));
this.writeToFile(entry);
}
logRequest(operation, params) {
this.log(operation, {
type: 'REQUEST',
params: this.sanitizeParams(params)
});
}
logResponse(operation, response) {
this.log(operation, {
type: 'RESPONSE',
response: this.summarizeResponse(response)
});
}
logError(operation, error) {
this.log(operation, {
type: 'ERROR',
errorCode: error.errorCode,
parameter: error.parameter,
identifier: error.identifier,
key: error.key,
rateLimitDuration: error.rateLimitDuration,
message: error.message,
stack: error.stack
});
}
sanitizeParams(params) {
const sanitized = { ...params };
if (sanitized.token) sanitized.token = '[REDACTED]';
if (sanitized.password) sanitized.password = '[REDACTED]';
if (sanitized.content && sanitized.content.length > 500) {
sanitized.content = sanitized.content.substring(0, 500) + '...[truncated]';
}
return sanitized;
}
summarizeResponse(response) {
if (!response) return null;
if (response.guid) {
return {
guid: response.guid,
title: response.title,
created: response.created,
updated: response.updated,
contentLength: response.content?.length
};
}
if (Array.isArray(response)) {
return {
count: response.length,
items: response.slice(0, 3).map(item => ({
guid: item.guid,
title: item.title || item.name
}))
};
}
return response;
}
writeToFile(entry) {
const filePath = path.join(this.logDir, this.logFile);
const line = JSON.stringify(entry) + '\n';
try {
fs.appendFileSync(filePath, line);
this.rotateIfNeeded(filePath);
} catch (error) {
console.error('Failed to write debug log:', error.message);
}
}
rotateIfNeeded(filePath) {
try {
const stats = fs.statSync(filePath);
if (stats.size > this.maxLogSize) {
const rotated = `${filePath}.${Date.now()}`;
fs.renameSync(filePath, rotated);
}
} catch (error) {
}
}
}
module.exports = EvernoteDebugLogger;
Step 2: Instrumented Client Wrapper
const Evernote = require('evernote');
const EvernoteDebugLogger = require('./debug-logger');
class InstrumentedEvernoteClient {
constructor(client, logger = null) {
this.client = client;
this.logger = logger || new EvernoteDebugLogger();
this._noteStore = null;
this._userStore = null;
}
get noteStore() {
if (!this._noteStore) {
this._noteStore = this.wrapStore(
this.client.getNoteStore(),
'NoteStore'
);
}
return this._noteStore;
}
get userStore() {
if (!this._userStore) {
this._userStore = .(
..(),
);
}
.;
}
() {
logger = .;
(store, {
() {
original = target[prop];
( original !== ) {
original;
}
(...args) => {
operation = ;
startTime = .();
logger.(operation, {
: args.( ({
: i,
: arg,
: arg === ? arg : (arg).(, )
}))
});
{
result = original.(target, args);
duration = .() - startTime;
logger.(operation, {
duration,
result
});
result;
} (error) {
duration = .() - startTime;
logger.(operation, {
duration,
error
});
error;
}
};
}
});
}
}
. = ;
Step 3: ENML Validator
class ENMLValidator {
static validate(content) {
const errors = [];
const warnings = [];
if (!content.includes('<?xml version="1.0"')) {
errors.push({
type: 'MISSING_XML_DECLARATION',
message: 'Missing XML declaration',
fix: 'Add: <?xml version="1.0" encoding="UTF-8"?>'
});
}
if (!content.includes('<!DOCTYPE en-note')) {
errors.push({
type: 'MISSING_DOCTYPE',
message: 'Missing DOCTYPE declaration',
fix: 'Add: <!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'
});
}
if (!/<en-note[^>]*>/.test(content)) {
errors.push({
type: 'MISSING_ROOT',
message: 'Missing <en-note> root element',
fix: 'Wrap content in <en-note>...</en-note>'
});
}
if (!content.includes('</en-note>')) {
errors.push({
type: 'UNCLOSED_ROOT',
: ,
:
});
}
forbidden = [
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : }
];
forbidden.( {
(pattern.(content)) {
errors.({
: ,
: ,
:
});
}
});
forbiddenAttrs = [
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : }
];
forbiddenAttrs.( {
(pattern.(content)) {
errors.({
: ,
: ,
:
});
}
});
voidElements = [, , , , , ];
unclosedPattern = ;
unclosed = content.(unclosedPattern);
(unclosed) {
unclosed.( {
warnings.({
: ,
: ,
:
});
});
}
mediaPattern = ;
match;
((match = mediaPattern.(content)) !== ) {
hash = match[];
(!.(hash)) {
errors.({
: ,
: ,
:
});
}
}
sizeBytes = .(content, );
sizeMB = sizeBytes / ( * );
(sizeMB > ) {
warnings.({
: ,
: ,
:
});
}
{
: errors. === ,
errors,
warnings,
: {
sizeBytes,
: sizeMB.(),
: .(content),
: .(content),
: .(content)
}
};
}
() {
fixed = content;
(!fixed.()) {
fixed = + fixed;
}
(!fixed.()) {
fixed = fixed.(
,
);
}
fixed = fixed.(, );
fixed = fixed.(, );
fixed = fixed.(, );
fixed = fixed.(, );
fixed = fixed.(, );
fixed = fixed.(, );
fixed;
}
}
. = ;
Step 4: Token Inspector
class TokenInspector {
static async inspect(client) {
const userStore = client.getUserStore();
const noteStore = client.getNoteStore();
const results = {
timestamp: new Date().toISOString(),
user: null,
accounting: null,
businessUser: null,
notebooks: null,
tags: null,
errors: []
};
try {
const user = await userStore.getUser();
results.user = {
id: user.id,
username: user.username,
email: user.email,
name: user.name,
timezone: user.timezone,
privilege: this.privilegeToString(user.privilege),
created: new Date(user.created),
: (user.),
: user.
};
(user.) {
results. = {
: .(user..),
: .(user..),
: .(
user.. - user..
),
: (user..),
: user.. ?
(user..) :
};
}
} (error) {
results..({
: ,
: error.
});
}
{
notebooks = noteStore.();
results. = {
: notebooks.,
: ,
: - notebooks.
};
} (error) {
results..({
: ,
: error.
});
}
{
tags = noteStore.();
results. = {
: tags.,
: ,
: - tags.
};
} (error) {
results..({
: ,
: error.
});
}
results;
}
() {
map = {
: ,
: ,
: ,
: ,
: ,
:
};
map[privilege] || ;
}
() {
(!bytes) ;
units = [, , , ];
i = ;
(bytes >= && i < units. - ) {
bytes /= ;
i++;
}
;
}
}
. = ;
Step 5: Diagnostic CLI
require('dotenv').config();
const Evernote = require('evernote');
const TokenInspector = require('../utils/token-inspector');
const ENMLValidator = require('../utils/enml-validator');
async function runDiagnostics() {
console.log('=== Evernote Diagnostic Report ===\n');
console.log('1. Environment Check');
console.log('-------------------');
console.log('EVERNOTE_ACCESS_TOKEN:', process.env.EVERNOTE_ACCESS_TOKEN ? 'Set' : 'NOT SET');
console.log('EVERNOTE_SANDBOX:', process.env.EVERNOTE_SANDBOX || 'Not set (defaulting to production)');
console.log();
if (!process.env.EVERNOTE_ACCESS_TOKEN) {
console.();
process.();
}
.();
.();
client = .({
: process..,
: process.. ===
});
{
inspection = .(client);
(inspection.) {
.(, inspection..);
.(, inspection..);
.(, inspection..);
}
(inspection.) {
.(, inspection.., );
.(, inspection..);
}
(inspection.) {
.(, inspection.., , inspection..);
}
(inspection.) {
.(, inspection..);
}
(inspection.. > ) {
.();
inspection..( .());
}
} (error) {
.(, error.);
(error. === ) {
.();
}
(error. === ) {
.();
}
process.();
}
.();
.();
testContent = ;
validation = .(testContent);
.(, validation. ? : );
(!validation.) {
validation..( .());
}
.();
}
().(.);
Step 6: Package.json Scripts
{
"scripts": {
"diagnose": "node scripts/diagnose.js",
"diagnose:verbose": "EVERNOTE_DEBUG=true node scripts/diagnose.js",
"validate:enml": "node -e \"require('./utils/enml-validator').validate(require('fs').readFileSync(process.argv[1], 'utf8'))\" --"
}
}
Output
- Debug logger with request/response tracking
- Instrumented client wrapper for automatic logging
- ENML validator with auto-fix capability
- Token and account inspector
- Diagnostic CLI script
Usage
npm run diagnose
npm run diagnose:verbose
npm run validate:enml note-content.xml
Error Handling
| Issue | Diagnostic | Solution |
|---|
| Auth failures | Check token in logs | Re-authenticate |
| ENML errors | Use ENMLValidator | Auto-fix with validator |
| Rate limits | Check request frequency in logs | Add delays |
| Missing data | Inspect response logs | Check API parameters |
Resources
Next Steps
For rate limit handling, see evernote-rate-limits.