How to log errors in Vue
Logging errors in Vue applications helps track bugs, monitor application health, and debug issues in production. As the creator of CoreUI with over 12 years of Vue.js experience since 2014, I’ve implemented comprehensive logging systems in enterprise applications. Vue’s global error handler combined with logging services captures errors with context for analysis and debugging. This approach provides visibility into application errors and helps maintain production stability.
Use Vue global error handler with custom loggers to capture and track errors in production applications.
Custom logger service:
// logger.js
class Logger {
constructor() {
this.logs = []
}
log(message, data = {}) {
const entry = {
level: 'info',
message,
data,
timestamp: new Date().toISOString(),
url: window.location.href
}
this.logs.push(entry)
console.log(message, data)
this.sendToServer(entry)
}
error(message, error, context = {}) {
const entry = {
level: 'error',
message,
error: {
message: error.message,
stack: error.stack,
name: error.name
},
context,
timestamp: new Date().toISOString(),
url: window.location.href,
userAgent: navigator.userAgent
}
this.logs.push(entry)
console.error(message, error, context)
this.sendToServer(entry)
}
warn(message, data = {}) {
const entry = {
level: 'warn',
message,
data,
timestamp: new Date().toISOString()
}
this.logs.push(entry)
console.warn(message, data)
}
async sendToServer(entry) {
try {
await fetch('/api/logs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(entry)
})
} catch (err) {
console.error('Failed to send log:', err)
}
}
getLogs() {
return this.logs
}
}
export const logger = new Logger()
Setup in main.js:
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import { logger } from './logger'
const app = createApp(App)
// Global error handler
app.config.errorHandler = (err, instance, info) => {
logger.error('Vue Error', err, {
component: instance?.$options.name || 'Unknown',
info,
props: instance?.$props
})
}
// Global warn handler
app.config.warnHandler = (msg, instance, trace) => {
logger.warn('Vue Warning', {
message: msg,
component: instance?.$options.name,
trace
})
}
// Provide logger globally
app.provide('logger', logger)
app.mount('#app')
Using logger in components:
<script setup>
import { inject, onMounted, onErrorCaptured } from 'vue'
const logger = inject('logger')
onMounted(() => {
logger.log('Component mounted', {
component: 'MyComponent'
})
})
onErrorCaptured((err, instance, info) => {
logger.error('Error in component', err, {
captured: true,
info
})
return false
})
const fetchData = async () => {
try {
const response = await fetch('/api/data')
const data = await response.json()
logger.log('Data fetched successfully')
} catch (err) {
logger.error('Failed to fetch data', err)
}
}
</script>
Enhanced logger with levels:
// enhancedLogger.js
const LogLevel = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3
}
class EnhancedLogger {
constructor(minLevel = LogLevel.INFO) {
this.minLevel = minLevel
this.maxLogs = 100
this.logs = []
}
debug(message, data) {
this.log(LogLevel.DEBUG, 'DEBUG', message, data)
}
info(message, data) {
this.log(LogLevel.INFO, 'INFO', message, data)
}
warn(message, data) {
this.log(LogLevel.WARN, 'WARN', message, data)
}
error(message, error, context) {
this.log(LogLevel.ERROR, 'ERROR', message, {
error: this.serializeError(error),
context
})
}
log(level, levelName, message, data) {
if (level < this.minLevel) return
const entry = {
level: levelName,
message,
data,
timestamp: Date.now(),
url: window.location.href
}
this.logs.push(entry)
if (this.logs.length > this.maxLogs) {
this.logs.shift()
}
this.consoleLog(levelName, message, data)
if (level >= LogLevel.ERROR) {
this.sendToServer(entry)
}
}
serializeError(error) {
return {
message: error.message,
stack: error.stack,
name: error.name,
...error
}
}
consoleLog(level, message, data) {
const method = level.toLowerCase()
if (console[method]) {
console[method](`[${level}] ${message}`, data)
}
}
async sendToServer(entry) {
try {
await fetch('/api/logs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(entry)
})
} catch (err) {
console.error('Log send failed:', err)
}
}
exportLogs() {
return JSON.stringify(this.logs, null, 2)
}
}
export const logger = new EnhancedLogger(
import.meta.env.MODE === 'production' ? LogLevel.WARN : LogLevel.DEBUG
)
Performance logging:
// performanceLogger.js
export class PerformanceLogger {
static measure(name, fn) {
const start = performance.now()
const result = fn()
const duration = performance.now() - start
logger.info(`Performance: ${name}`, {
duration: `${duration.toFixed(2)}ms`
})
return result
}
static async measureAsync(name, fn) {
const start = performance.now()
const result = await fn()
const duration = performance.now() - start
logger.info(`Performance: ${name}`, {
duration: `${duration.toFixed(2)}ms`
})
return result
}
}
// Usage
const data = await PerformanceLogger.measureAsync('Fetch Users', async () => {
return await fetch('/api/users').then(r => r.json())
})
Best Practice Note
Log errors with enough context to reproduce—component name, props, user actions. Use log levels (debug, info, warn, error) appropriately. Send only error-level logs to server to reduce overhead. Include timestamp, URL, and user agent in logs. Limit in-memory logs to prevent memory leaks. Batch log submissions to reduce network requests. This is how we implement logging in CoreUI Vue applications—comprehensive error tracking with context, appropriate log levels, and integration with monitoring services for production debugging.



