The jspb.debug namespace provides development-time utilities for debugging and inspecting Protocol Buffer messages. These utilities help developers understand message structure and content during development.
const { debug } = require('google-protobuf');
// Create human-readable representation of a message
const readableObject = debug.dump(message);
console.log(readableObject);
// Example output for a user message:
/*
{
name: "John Doe",
age: 25,
email: "john@example.com",
addresses: [
{
street: "123 Main St",
city: "Anytown",
zipCode: "12345"
},
{
street: "456 Oak Ave",
city: "Other City",
zipCode: "67890"
}
],
preferences: {
theme: "dark",
notifications: true,
language: "en"
}
}
*/Type Definitions:
/**
* @param {!jspb.Message} message Protocol Buffer message instance
* @return {?Object} Human-readable object representation, or null in production
*/Important Notes:
null in production builds or when code is minifiedThe debug utilities use reflection to examine message structure:
const { debug } = require('google-protobuf');
// Example message with various field types
class PersonMessage extends Message {
getName() { return this.name_; }
setName(value) { this.name_ = value; }
getAge() { return this.age_; }
setAge(value) { this.age_ = value; }
getEmailsList() { return this.emails_; }
setEmailsList(value) { this.emails_ = value; }
getAttributesMap() { return this.attributes_; }
getAddress() { return this.address_; }
setAddress(value) { this.address_ = value; }
}
// Create and populate message
const person = new PersonMessage();
person.setName("Alice Smith");
person.setAge(30);
person.setEmailsList(["alice@work.com", "alice@personal.com"]);
const address = new AddressMessage();
address.setStreet("789 Pine St");
address.setCity("Springfield");
person.setAddress(address);
const attrs = person.getAttributesMap();
attrs.set("department", "Engineering");
attrs.set("level", "Senior");
// Debug dump shows structured representation
const dump = debug.dump(person);
/*
{
name: "Alice Smith",
age: 30,
emailsList: ["alice@work.com", "alice@personal.com"],
address: {
street: "789 Pine St",
city: "Springfield"
},
attributesMap: {
department: "Engineering",
level: "Senior"
}
}
*/The debug utilities automatically format field names for readability:
const { debug } = require('google-protobuf');
// Generated getters typically use TitleCase
class ExampleMessage extends Message {
getUserName() { return this.user_name_; }
setUserName(value) { this.user_name_ = value; }
getAccountID() { return this.account_id_; }
setAccountID(value) { this.account_id_ = value; }
getIsActiveUser() { return this.is_active_user_; }
setIsActiveUser(value) { this.is_active_user_ = value; }
}
const message = new ExampleMessage();
message.setUserName("john_doe");
message.setAccountID("12345");
message.setIsActiveUser(true);
const dump = debug.dump(message);
/*
Field names are converted from TitleCase getters to camelCase:
{
userName: "john_doe", // getUserName -> userName
accountID: "12345", // getAccountID -> accountID
isActiveUser: true // getIsActiveUser -> isActiveUser
}
*/Debug utilities recursively process nested messages and complex structures:
const { debug } = require('google-protobuf');
// Complex nested message structure
class CompanyMessage extends Message {
getName() { return this.name_; }
getDepartmentsList() { return this.departments_; }
getOfficesMap() { return this.offices_; }
}
class DepartmentMessage extends Message {
getName() { return this.name_; }
getEmployeesList() { return this.employees_; }
getBudget() { return this.budget_; }
}
class EmployeeMessage extends Message {
getName() { return this.name_; }
getPosition() { return this.position_; }
getSalary() { return this.salary_; }
}
// Build complex message hierarchy
const company = new CompanyMessage();
company.setName("TechCorp");
const engineering = new DepartmentMessage();
engineering.setName("Engineering");
engineering.setBudget(1000000);
const alice = new EmployeeMessage();
alice.setName("Alice Johnson");
alice.setPosition("Senior Developer");
alice.setSalary(85000);
const bob = new EmployeeMessage();
bob.setName("Bob Smith");
bob.setPosition("Tech Lead");
bob.setSalary(95000);
engineering.setEmployeesList([alice, bob]);
company.setDepartmentsList([engineering]);
// Debug dump shows full hierarchy
const dump = debug.dump(company);
/*
{
name: "TechCorp",
departmentsList: [
{
name: "Engineering",
budget: 1000000,
employeesList: [
{
name: "Alice Johnson",
position: "Senior Developer",
salary: 85000
},
{
name: "Bob Smith",
position: "Tech Lead",
salary: 95000
}
]
}
]
}
*/Maps are converted to plain JavaScript objects for easier inspection:
const { debug, Map } = require('google-protobuf');
class ConfigMessage extends Message {
getSettingsMap() { return this.settings_; }
getServersMap() { return this.servers_; }
}
class ServerMessage extends Message {
getHost() { return this.host_; }
getPort() { return this.port_; }
getStatus() { return this.status_; }
}
// Create message with map fields
const config = new ConfigMessage();
// String-to-string map
const settings = config.getSettingsMap();
settings.set("debug_mode", "true");
settings.set("log_level", "info");
settings.set("timeout", "30");
// String-to-message map
const servers = config.getServersMap();
const webServer = new ServerMessage();
webServer.setHost("web.example.com");
webServer.setPort(80);
webServer.setStatus("active");
const apiServer = new ServerMessage();
apiServer.setHost("api.example.com");
apiServer.setPort(443);
apiServer.setStatus("maintenance");
servers.set("web", webServer);
servers.set("api", apiServer);
// Debug dump converts maps to objects
const dump = debug.dump(config);
/*
{
settingsMap: {
debug_mode: "true",
log_level: "info",
timeout: "30"
},
serversMap: {
web: {
host: "web.example.com",
port: 80,
status: "active"
},
api: {
host: "api.example.com",
port: 443,
status: "maintenance"
}
}
}
*/Extension fields are included in debug output when available:
const { debug, ExtensionFieldInfo } = require('google-protobuf');
// Define extension field
const customExtension = new ExtensionFieldInfo(
1001, // Field number
{custom_data: 0}, // Field name object
null, // No constructor (primitive)
null, // No toObject function
0 // Not repeated
);
const userExtension = new ExtensionFieldInfo(
1002,
{user_metadata: 0},
UserMetadataMessage, // Message constructor
null, // Use default toObject
0
);
// Use extensions
const message = new BaseMessage();
message.setExtension(customExtension, "custom value");
const metadata = new UserMetadataMessage();
metadata.setCreatedBy("admin");
metadata.setCreatedAt("2023-01-01");
message.setExtension(userExtension, metadata);
// Debug dump includes extensions
const dump = debug.dump(message);
/*
{
// Regular fields...
name: "Base Message",
// Extensions appear in the dump
customData: "custom value",
userMetadata: {
createdBy: "admin",
createdAt: "2023-01-01"
}
}
*/const { debug } = require('google-protobuf');
// Quick message inspection during development
function debugMessage(message, label = "Message") {
const dump = debug.dump(message);
if (dump) {
console.group(label);
console.log(JSON.stringify(dump, null, 2));
console.groupEnd();
} else {
console.log(`${label}: [Debug info not available in production]`);
}
}
// Usage in development
const user = new UserMessage();
user.setName("Test User");
user.setEmail("test@example.com");
debugMessage(user, "Created User");
// Modify and debug again
user.setAge(25);
debugMessage(user, "Updated User");const { debug } = require('google-protobuf');
// Test helper for message comparison
function assertMessagesEqual(expected, actual, testName) {
const expectedDump = debug.dump(expected);
const actualDump = debug.dump(actual);
if (expectedDump && actualDump) {
const expectedJson = JSON.stringify(expectedDump, null, 2);
const actualJson = JSON.stringify(actualDump, null, 2);
if (expectedJson !== actualJson) {
console.error(`Test failed: ${testName}`);
console.error("Expected:", expectedJson);
console.error("Actual:", actualJson);
throw new Error(`Message mismatch in ${testName}`);
}
} else {
// Fallback to binary comparison in production
const expectedBytes = expected.serializeBinary();
const actualBytes = actual.serializeBinary();
if (expectedBytes.length !== actualBytes.length ||
!expectedBytes.every((b, i) => b === actualBytes[i])) {
throw new Error(`Message mismatch in ${testName}`);
}
}
}
// Usage in tests
const originalMessage = createTestMessage();
const serialized = originalMessage.serializeBinary();
const deserialized = TestMessage.deserializeBinary(serialized);
assertMessagesEqual(originalMessage, deserialized, "Serialization Round-trip");const { debug } = require('google-protobuf');
// Enhanced error reporting with message state
function processMessage(message) {
try {
// Process message logic
return performComplexOperation(message);
} catch (error) {
// Include message state in error reporting
const messageState = debug.dump(message);
console.error("Processing failed for message:", messageState);
console.error("Error:", error.message);
console.error("Stack:", error.stack);
throw new Error(`Message processing failed: ${error.message}`);
}
}// The debug utilities check for code optimization
const { debug } = require('google-protobuf');
function safeDebugDump(message) {
const dump = debug.dump(message);
if (dump === null) {
// Production build - debug info not available
return {
message: "[Debug information not available in optimized build]",
type: message.constructor.name || "Unknown",
serialized: message.serializeBinary().length + " bytes"
};
}
return dump;
}
// Always safe to call
console.log("Message state:", safeDebugDump(message));const { debug } = require('google-protobuf');
// Only use debug utilities in development
const isDevelopment = process.env.NODE_ENV === 'development';
function conditionalDebug(message, label) {
if (!isDevelopment) {
return; // Skip debug output in production
}
const dump = debug.dump(message);
if (dump) {
console.log(`[DEBUG] ${label}:`, dump);
}
}
// Safe for production deployment
conditionalDebug(userMessage, "User Registration");// Debug operations have minimal overhead when optimized away
const { debug } = require('google-protobuf');
function benchmarkMessage(message, iterations = 1000) {
const start = performance.now();
for (let i = 0; i < iterations; i++) {
const dump = debug.dump(message); // No-op in production
// Prevent optimization
if (dump) {
doSomethingWithDump(dump);
}
}
const end = performance.now();
console.log(`Debug operations: ${end - start}ms for ${iterations} calls`);
}
// Minimal overhead in production builds
benchmarkMessage(testMessage);// Debug dumps create temporary objects
const { debug } = require('google-protobuf');
function processLargeMessageList(messages) {
return messages.map(message => {
// Process message
const result = processMessage(message);
// Debug in development only - avoid memory overhead in production
if (process.env.NODE_ENV === 'development') {
const dump = debug.dump(result);
if (dump && Object.keys(dump).length > 10) {
console.warn("Large message detected:", Object.keys(dump).length, "fields");
}
}
return result;
});
}