performance-monitor-logger
Version:
Performance monitor logger for Angular applications
915 lines (905 loc) • 40.9 kB
JavaScript
import { Component, NgModule, ɵɵdefineInjectable, Injectable } from '@angular/core';
import { HttpResponse } from '@angular/common/http';
import { tap } from 'rxjs/operators';
import { __awaiter } from 'tslib';
class PerformanceMonitorComponent {
constructor() { }
ngOnInit() { }
}
PerformanceMonitorComponent.decorators = [
{ type: Component, args: [{
selector: 'lib-performanceMonitorLogger',
template: ` <p>performance-monitor-logger works!</p> `
},] }
];
PerformanceMonitorComponent.ctorParameters = () => [];
// performance-monitor.module.ts
class PerformanceMonitorModule {
}
PerformanceMonitorModule.decorators = [
{ type: NgModule, args: [{
declarations: [PerformanceMonitorComponent],
imports: [],
exports: [PerformanceMonitorComponent],
},] }
];
class PerformanceMonitorService {
constructor() { }
}
PerformanceMonitorService.ɵprov = ɵɵdefineInjectable({ factory: function PerformanceMonitorService_Factory() { return new PerformanceMonitorService(); }, token: PerformanceMonitorService, providedIn: "root" });
PerformanceMonitorService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
PerformanceMonitorService.ctorParameters = () => [];
/**
* 📊 SISTEMA DE LOGS DE PERFORMANCE
*
* Sistema completo de monitoramento de performance para aplicações Angular
* com feedback visual através de emojis e relatórios automáticos.
*
* Características:
* - 📈 Monitoramento em Tempo Real
* - 🟢🟡🔴 Classificação Visual por Performance
* - ⏰ Relatórios Automáticos após Inatividade
* - 📊 Análise Detalhada com Recomendações
* - 🛠️ API Pública para Controle Manual
*
* @author Pedro Henrique dos Santos Leuchs
* @version 1.0.19
* @since 2025-07-11
*/
class LogsPerformatico {
// 🆕 Adiciona requests para registrar requisições HTTP
constructor(serviceName = 'Service') {
this.monitoramentoAtivo = true; // Começa ativo na primeira vez
// 📊 Propriedades do Sistema
this.performanceTimers = new Map();
this.performanceSummary = new Map();
this.httpRequests = new Map();
// 🆕 Stack para contexto de função monitorada
this.functionContextStack = [];
this.serviceName = serviceName;
}
/**
* Registra requisição HTTP associando à função monitorada ativa (se houver)
* @param payload Dados da requisição
*/
registerHttpRequest(authReq, tempo, error = false) {
var _a, _b;
const nome = ((_a = authReq.body) === null || _a === void 0 ? void 0 : _a.name) ||
this.extrairNomeDeSQL((_b = authReq.body) === null || _b === void 0 ? void 0 : _b.sqlInstruction) ||
'(desconhecido)';
const atual = this.httpRequests.get(nome) || {
tempoTotal: 0,
execucoes: 0,
errors: 0,
};
atual.tempoTotal += tempo;
atual.execucoes += 1;
if (error)
atual.errors += 1;
this.httpRequests.set(nome, atual);
}
// Função auxiliar para extrair nome da função de um SQL, ex: PACK_PARMGEN.CONSULTA_PARAMETRO(...)
extrairNomeDeSQL(sql) {
if (!sql)
return null;
const match = sql.match(/([A-Z0-9_]+\.[A-Z0-9_]+)/i);
return match ? match[1] : null;
}
/**
* 🚀 Mensagem de boas-vindas do sistema
*/
showWelcomeMessage() {
console.log(`%cSISTEMA DE MONITORAMENTO DE PERFORMANCE`, 'color: #bbc7c8ff; font-size: 18px; font-weight: bold; text-shadow: 1px 1px #000; background-color: #43248bff; border-radius: 5px; padding: 10px 20px;');
}
/**
* ⏱️ Inicia cronômetro para uma operação
* @param operationName Nome da operação a ser monitorada
*/
startTimer(operationName) {
if (!this.monitoramentoAtivo)
return;
// Se for a primeira operação, abre borda visual
if (this.performanceTimers.size === 0 &&
this.performanceSummary.size === 0) {
const border = '═'.repeat(56);
console.log(`%cINÍCIO DO MONITORAMENTO DE PERFORMANCE`, 'color: #bbc7c8ff; font-size: 18px; font-weight: bold; text-shadow: 1px 1px #000; background-color: #43248bff; border-radius: 5px; padding: 10px 20px;');
}
this.performanceTimers.set(operationName, Date.now());
// Empilha contexto de função monitorada
this.functionContextStack.push(operationName);
console.log(`║ [INÍCIO] ${operationName} - ${new Date().toLocaleTimeString()}`);
}
/**
* 🏁 Finaliza cronômetro e exibe resultado com emoji
* @param operationName Nome da operação
* @param customMessage Mensagem customizada opcional (ex: para erros)
* @param customThresholds Thresholds customizados para esta operação específica
*/
endTimer(operationName, customMessage, customThresholds) {
var _a, _b;
if (!this.monitoramentoAtivo)
return;
const startTime = this.performanceTimers.get(operationName);
if (!startTime) {
console.warn(`⚠️ Timer não encontrado para: ${operationName}`);
return;
}
const elapsedTime = Date.now() - startTime;
this.performanceTimers.delete(operationName);
// Desempilha contexto de função monitorada
if (this.functionContextStack[this.functionContextStack.length - 1] ===
operationName) {
this.functionContextStack.pop();
}
// Determina status baseado no tempo (com thresholds customizados se fornecidos ou dinâmicos)
const { emoji, status, color } = this.getPerformanceStatus(elapsedTime, customThresholds, operationName);
// Log com emoji e cor (com mensagem customizada se fornecida)
const message = customMessage
? `${operationName} - ${customMessage}`
: `${operationName}`;
const fixedStatus = `[${status}]`.padEnd(5); // Ex: '[OK] '
const fixedMessage = message.padEnd(10); // Nome da função
const fixedTime = elapsedTime.toString().padStart(5); // 5 para " 16ms", "2193ms"
console.log(`║ %c${emoji} ${fixedStatus} ${fixedMessage} - ${fixedTime}ms `, color);
// Mostra thresholds customizados se estiverem sendo usados
if (customThresholds &&
(customThresholds.ok ||
customThresholds.lento ||
customThresholds.critico)) {
console.log(`⚙️ Thresholds customizados: OK<${(_a = customThresholds.ok) !== null && _a !== void 0 ? _a : 500}ms, LENTO<${(_b = customThresholds.lento) !== null && _b !== void 0 ? _b : 2000}ms`);
}
// Atualiza sumário (com thresholds customizados se fornecidos)
this.updatePerformanceSummary(operationName, elapsedTime, customThresholds);
}
/**
* 🎨 Determina status de performance baseado no tempo
* @param elapsedTime Tempo decorrido em milissegundos
* @param customThresholds Thresholds customizados para esta operação específica
* @param operationName Nome da operação (para calcular thresholds dinâmicos)
* @returns Objeto com emoji, status e cor
*/
getPerformanceStatus(elapsedTime, customThresholds, operationName) {
var _a, _b, _c;
let okThreshold;
let lentoThreshold;
let criticoThreshold;
if (customThresholds &&
(customThresholds.ok ||
customThresholds.lento ||
customThresholds.critico)) {
// Usa thresholds customizados se fornecidos
okThreshold = (_a = customThresholds.ok) !== null && _a !== void 0 ? _a : 500;
lentoThreshold = (_b = customThresholds.lento) !== null && _b !== void 0 ? _b : 2000;
criticoThreshold = (_c = customThresholds.critico) !== null && _c !== void 0 ? _c : Number.MAX_SAFE_INTEGER;
}
else if (operationName) {
// Calcula thresholds dinâmicos baseados na quantidade de execuções
const stats = this.performanceSummary.get(operationName);
// Corrige: soma +1 para considerar a execução atual
const executionCount = stats ? stats.count + 1 : 1;
const dynamicThresholds = this.calculateDynamicThresholds(executionCount);
okThreshold = dynamicThresholds.ok;
lentoThreshold = dynamicThresholds.lento;
criticoThreshold = dynamicThresholds.critico;
}
else {
// Usa thresholds padrões
okThreshold = 500;
lentoThreshold = 2000;
criticoThreshold = Number.MAX_SAFE_INTEGER;
}
if (elapsedTime < okThreshold) {
return {
emoji: '🟢',
status: 'OK',
color: 'color: green; font-weight: bold;',
};
}
else if (elapsedTime < lentoThreshold) {
return {
emoji: '🟡',
status: 'LENTO',
color: 'color: orange; font-weight: bold;',
};
}
else {
return {
emoji: '🔴',
status: 'CRÍTICO',
color: 'color: red; font-weight: bold;',
};
}
}
/**
* 📈 Atualiza estatísticas de performance para o sumário
* @param operationName Nome da operação
* @param elapsedTime Tempo decorrido em milissegundos
* @param customThresholds Thresholds customizados para armazenar (opcional)
*/
updatePerformanceSummary(operationName, elapsedTime, customThresholds) {
const existing = this.performanceSummary.get(operationName);
if (existing) {
existing.count++;
existing.totalTime += elapsedTime;
existing.minTime = Math.min(existing.minTime, elapsedTime);
existing.maxTime = Math.max(existing.maxTime, elapsedTime);
// Atualiza thresholds customizados se fornecidos
if (customThresholds &&
(customThresholds.ok ||
customThresholds.lento ||
customThresholds.critico)) {
existing.customThresholds = customThresholds;
}
}
else {
this.performanceSummary.set(operationName, {
count: 1,
totalTime: elapsedTime,
minTime: elapsedTime,
maxTime: elapsedTime,
customThresholds: customThresholds,
});
}
// Não faz mais nada aqui, relatório só manual
}
/**
* 📊 Exibe relatório resumido de todas as operações (compactado)
*/
showPerformanceSummary(showFullDetails = false) {
// Fecha borda do monitoramento ao gerar relatório
console.log('%c RELATÓRIO DE PERFORMANCE', 'color: #bbc7c8ff; font-size: 18px; font-weight: bold; text-shadow: 1px 1px #000; background-color: #43248bff; border-radius: 5px; padding: 10px 20px;');
if (this.performanceSummary.size === 0) {
console.log('%c Nenhuma operação monitorada ainda.', 'color: gray; font-style: italic;');
return;
}
const sortedEntries = Array.from(this.performanceSummary.entries()).sort((a, b) => b[1].maxTime - a[1].maxTime);
const table = Object.fromEntries(sortedEntries.map(([operation, stats]) => {
const avgTime = Math.round(stats.totalTime / stats.count);
const { emoji, status } = this.getPerformanceStatus(avgTime, stats.customThresholds, operation);
return [
operation,
{
Execuções: stats.count,
Média: `${avgTime}ms (${status}) ${emoji}`.trim(),
Mínimo: `${stats.minTime}ms`,
Máximo: `${stats.maxTime}ms`,
Total: `${stats.totalTime}ms`,
},
];
}));
console.table(table);
// Removido: agora a análise detalhada é chamada apenas pelo botão
}
/**
* 📑 Exibe relatório detalhado de requisições HTTP agrupadas por função
*/
showHttpRequestsReport() {
console.log(`%cRELATÓRIO DETALHADO DE REQUISIÇÕES HTTP`, 'color: #bbc7c8ff; font-size: 18px; font-weight: bold; text-shadow: 1px 1px #000; background-color: #43248bff; border-radius: 5px; padding: 10px 20px;');
if (this.httpRequests.size === 0) {
console.log('Nenhuma requisição HTTP registrada.');
return;
}
const tabela = Object.fromEntries(Array.from(this.httpRequests.entries())
.sort((a, b) => b[1].tempoTotal - a[1].tempoTotal)
.map(([nome, dados]) => [
nome,
{
Execuções: dados.execucoes,
Média: `${Math.round(dados.tempoTotal / dados.execucoes)}ms`,
'Tempo Total': `${Math.round(dados.tempoTotal)}ms`,
Erros: dados.errors,
},
]));
console.table(tabela);
}
/**
* 🔍 Gera análise detalhada de performance
*/
generateDetailedAnalysis() {
// Título discreto
console.log('%cANÁLISE DETALHADA DE PERFORMANCE', 'color: #bbc7c8ff; font-size: 18px; font-weight: bold; text-shadow: 1px 1px #000; background-color: #43248bff; border-radius: 5px; padding: 10px 20px;');
console.log('');
if (this.performanceSummary.size === 0) {
console.log('%cNenhuma operação registrada para análise.', 'color: #bbb; font-style: italic; font-size: 14px; padding: 4px 0 4px 10px;');
return;
}
const { criticalOperations, slowOperations, fastOperations } = this.categorizeOperations();
// Helper para bloco discreto com texto claro
const printBlock = (color, emoji, operation, avg, extra) => {
const style = `border-left: 4px solid ${color}; padding: 2px 0 2px 10px; margin: 2px 0 2px 0; font-size: 14px; color: #fff; background: none; font-weight: normal;`;
console.log(`%c${emoji} ${operation} - ${avg}ms (média) ${extra}`.trim(), style);
};
if (criticalOperations.length > 0) {
console.log('%c🔴 CRÍTICAS ( > 2000ms)', 'color: #fff; font-size: 14px; font-weight: bold; margin-top: 10px;');
criticalOperations.forEach(([operation, stats]) => {
const avg = Math.round(stats.totalTime / stats.count);
const { emoji } = this.getPerformanceStatus(avg, stats.customThresholds, operation);
printBlock('#c53030', emoji, operation, avg, '| otimizar urgente');
});
}
if (slowOperations.length > 0) {
console.log('%c🟡 LENTAS (500ms - 2000ms)', 'color: #fff; font-size: 14px; font-weight: bold; margin-top: 10px;');
slowOperations.forEach(([operation, stats]) => {
const avg = Math.round(stats.totalTime / stats.count);
const { emoji } = this.getPerformanceStatus(avg, stats.customThresholds, operation);
printBlock('#b7791f', emoji, operation, avg, '| pode ser melhorada');
});
}
if (fastOperations.length > 0) {
console.log('%c🟢 RÁPIDAS ( < 500ms)', 'color: #fff; font-size: 14px; font-weight: bold; margin-top: 10px;');
fastOperations.forEach(([operation, stats]) => {
const avg = Math.round(stats.totalTime / stats.count);
const { emoji } = this.getPerformanceStatus(avg, stats.customThresholds, operation);
printBlock('#38a169', emoji, operation, avg, '| excelente');
});
}
// Recomendações
console.log('%cRecomendações:', 'color: #fff; font-size: 13px; font-weight: bold; margin-top: 14px; padding: 2px 0 2px 0;');
if (criticalOperations.length > 0) {
console.log('%c• Priorize a otimização das operações críticas.', 'color: #fff; font-size: 13px; font-weight: normal;');
console.log('%c• Considere implementar cache ou otimizar consultas SQL para essas operações.', 'color: #fff; font-size: 13px; font-weight: normal;');
}
if (slowOperations.length > 0) {
console.log('%c• Operações lentas podem se beneficiar de índices no banco de dados.', 'color: #fff; font-size: 13px; font-weight: normal;');
}
if (criticalOperations.length === 0 && slowOperations.length === 0) {
console.log('%c• Todas as operações estão com performance excelente!', 'color: #fff; font-size: 13px; font-weight: normal;');
}
// Score
const totalOperations = this.performanceSummary.size;
const fastCount = fastOperations.length;
const performanceScore = totalOperations > 0
? Math.round((fastCount / totalOperations) * 100)
: 100;
let scoreColor = performanceScore >= 80
? '#38a169'
: performanceScore >= 60
? '#f6e05e'
: '#c53030';
let scoreMsg = performanceScore >= 80
? 'Excelente! A maioria das operações está performando bem.'
: performanceScore >= 60
? 'Médio. Há espaço para melhorias de performance.'
: 'Crítico. Muitas operações precisam de otimização urgente.';
console.log(`%cScore de performance: ${performanceScore}%`, `color: #fff; background: ${scoreColor}; font-size: 14px; font-weight: bold; margin-top: 10px; padding: 2px 8px; border-radius: 3px;`);
console.log(`%c${scoreMsg}`, `color: #fff; font-size: 13px; font-weight: normal; margin-bottom: 8px;`);
console.log('');
}
/**
* 📋 Categoriza operações por performance
* @returns Objeto com arrays de operações categorizadas
*/
categorizeOperations() {
const criticalOperations = Array.from(this.performanceSummary.entries()).filter(([_, stats]) => stats.totalTime / stats.count > 2000);
const slowOperations = Array.from(this.performanceSummary.entries()).filter(([_, stats]) => {
const avg = stats.totalTime / stats.count;
return avg >= 500 && avg <= 2000;
});
const fastOperations = Array.from(this.performanceSummary.entries()).filter(([_, stats]) => stats.totalTime / stats.count < 500);
return { criticalOperations, slowOperations, fastOperations };
}
/**
* 💡 Exibe recomendações baseadas na análise
* @param criticalOperations Array de operações críticas
* @param slowOperations Array de operações lentas
*/
showRecommendations(criticalOperations, slowOperations) {
console.log('RECOMENDAÇÕES:');
if (criticalOperations.length > 0) {
console.log(' Priorize a otimização das operações críticas.');
console.log(' Considere implementar cache ou otimizar consultas SQL para essas operações.');
}
if (slowOperations.length > 0) {
console.log(' Operações lentas podem se beneficiar de índices no banco de dados.');
}
if (criticalOperations.length === 0 && slowOperations.length === 0) {
console.log(' Todas as operações estão com performance excelente!');
}
}
/**
* 📊 Exibe score de performance
* @param fastOperationsCount Número de operações rápidas
*/
showPerformanceScore(fastOperationsCount) {
const totalOperations = this.performanceSummary.size;
const performanceScore = Math.round((fastOperationsCount / totalOperations) * 100);
console.log(`SCORE DE PERFORMANCE: ${performanceScore}%`);
if (performanceScore >= 80) {
console.log('Excelente! A maioria das operações está performando bem.');
}
else if (performanceScore >= 60) {
console.log('Médio. Há espaço para melhorias de performance.');
}
else {
console.log('Crítico. Muitas operações precisam de otimização urgente.');
}
}
// ===== API PÚBLICA =====
/**
* 📊 Método público para exibir relatório de performance
* Pode ser chamado no console do navegador para análise
*/
getPerformanceReport() {
this.showPerformanceSummary();
}
/**
* 🧹 Método público para limpar dados de performance
* Útil para resetar estatísticas e começar nova análise
*/
resetPerformanceData() {
this.performanceTimers.clear();
this.performanceSummary.clear();
this.httpRequests.clear();
// Mensagem estilizada de limpeza
console.log('%c🧹 [CLEANUP] Dados de performance limpos', 'color: #fff; background: #43248bff; font-size: 13px; font-weight: bold; border-radius: 4px; padding: 4px 12px; margin: 6px 0;');
}
/**
* ⏰ Configura o delay para relatório por inatividade
* @param seconds Número de segundos de inatividade para mostrar relatório
*/
setInactivityDelay(seconds) {
// Desabilitado
console.log('⏰ Relatório por inatividade está desabilitado.');
}
/**
* 🔇 Desabilita relatório automático por inatividade
*/
disableInactivityReport() {
// Desabilitado
console.log('🔇 Relatório por inatividade já está desabilitado');
}
/**
* ⚙️ Configura o threshold para relatório automático
* @param threshold Número de operações para mostrar relatório automaticamente
*/
setAutoReportThreshold(threshold) {
// Desabilitado
console.log('⚙️ Threshold para relatório automático está desabilitado.');
}
/**
* 🔇 Desabilita relatório automático
*/
disableAutoReport() {
// Desabilitado
console.log('🔇 Relatório automático desabilitado');
}
/**
* 📈 Obtém estatísticas resumidas sem exibir no console
* @returns Objeto com estatísticas de performance
*/
getStatistics() {
const { criticalOperations, slowOperations, fastOperations } = this.categorizeOperations();
const totalOperations = this.performanceSummary.size;
let totalTime = 0;
let totalCount = 0;
this.performanceSummary.forEach((stats) => {
totalTime += stats.totalTime;
totalCount += stats.count;
});
const averageTime = totalCount > 0 ? Math.round(totalTime / totalCount) : 0;
const performanceScore = totalOperations > 0
? Math.round((fastOperations.length / totalOperations) * 100)
: 100;
return {
totalOperations,
averageTime,
fastOperations: fastOperations.length,
slowOperations: slowOperations.length,
criticalOperations: criticalOperations.length,
performanceScore,
};
}
/**
* 🎯 Obtém dados de uma operação específica
* @param operationName Nome da operação
* @returns Estatísticas da operação ou null se não encontrada
*/
getOperationStats(operationName) {
const baseStats = this.performanceSummary.get(operationName);
if (!baseStats)
return null;
// Extrai nomes das requisições HTTP como array de strings
const httpRequestEntry = this.httpRequests.get(operationName);
const httpCalls = httpRequestEntry ? [operationName] : [];
return Object.assign(Object.assign({}, baseStats), { httpCalls });
}
/**
* 📋 Lista todas as operações monitoradas
* @returns Array com nomes das operações
*/
getMonitoredOperations() {
return Array.from(this.performanceSummary.keys());
}
/**
* 🔄 Verifica se há timers ativos
* @returns true se há timers em execução
*/
hasActiveTimers() {
return this.performanceTimers.size > 0;
}
/**
* 🗑️ Limpa apenas os timers ativos (não as estatísticas)
*/
clearActiveTimers() {
this.performanceTimers.clear();
console.log('%c🗑️ [CLEANUP] Timers ativos limpos', 'color: #fff; background: #23272f; font-size: 13px; font-weight: bold; border-radius: 4px; padding: 4px 12px; margin: 6px 0;');
}
/**
* 💾 Exporta dados de performance para JSON
* @returns String JSON com todos os dados
*/
exportData() {
const data = {
timestamp: new Date().toISOString(),
serviceName: this.serviceName,
statistics: this.getStatistics(),
operations: Array.from(this.performanceSummary.entries()).map(([name, stats]) => (Object.assign(Object.assign({ name }, stats), { averageTime: Math.round(stats.totalTime / stats.count) }))),
};
return JSON.stringify(data, null, 2);
}
/**
* 🎯 Calcula thresholds dinâmicos baseados na quantidade de execuções
* @param executionCount Número de execuções da operação
* @returns Objeto com thresholds calculados dinamicamente
*/
calculateDynamicThresholds(executionCount) {
if (executionCount === 1) {
// Primeira execução - expectativas mais relaxadas
return {
ok: 1000,
lento: 2000,
critico: 2000,
};
}
else if (executionCount <= 5) {
// Até 5 execuções - sistema ainda aquecendo
return {
ok: 500,
lento: 1000,
critico: 1000,
};
}
else if (executionCount <= 10) {
// Até 10 execuções - sistema otimizado
return {
ok: 100,
lento: 250,
critico: 250,
};
}
else {
// Acima de 10 execuções - expectativas altas
return {
ok: 20,
lento: 60,
critico: 60,
};
}
}
injectMonitorButton() {
if (document.getElementById('btn-monitorar-performance'))
return;
// ====== ESTILO MODERNO E BOTÃO DE OLHO ======
const BUTTON_COLOR = '#23272f'; // cinza escuro elegante
const BUTTON_TEXT_COLOR = '#f3f4f6';
const BUTTON_RADIUS = '8px';
const BUTTON_FONT_SIZE = '13px';
const BUTTON_PADDING = '10px 5px';
const BUTTON_WIDTH = '210px';
const BUTTON_HEIGHT = '44px';
const BUTTON_BOX_SHADOW = '0 2px 12px #0002';
const BUTTON_BORDER = '1px solid #353a40';
// Container para animação
const container = document.createElement('div');
container.id = 'performance-monitor-container';
Object.assign(container.style, {
position: 'fixed',
bottom: '5px',
left: '50px',
zIndex: '9999',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'flex-end',
transition: 'transform 0.5s cubic-bezier(.4,2,.6,1)',
gap: '10px',
});
// Função para criar botões padronizados
const criarBotao = (id, texto) => {
const btn = document.createElement('button');
btn.id = id;
btn.innerText = texto;
Object.assign(btn.style, {
width: BUTTON_WIDTH,
height: BUTTON_HEIGHT,
background: BUTTON_COLOR,
color: BUTTON_TEXT_COLOR,
borderRadius: BUTTON_RADIUS,
border: BUTTON_BORDER,
padding: BUTTON_PADDING,
fontSize: BUTTON_FONT_SIZE,
fontWeight: '600',
cursor: 'pointer',
boxShadow: BUTTON_BOX_SHADOW,
letterSpacing: '0.01em',
textAlign: 'center',
transition: 'background 0.2s, color 0.2s, transform 0.5s cubic-bezier(.4,2,.6,1)',
});
btn.onmouseenter = () => {
btn.style.background = '#353a40';
btn.style.color = '#fff';
};
btn.onmouseleave = () => {
btn.style.background = BUTTON_COLOR;
btn.style.color = BUTTON_TEXT_COLOR;
};
return btn;
};
// Botões principais
const btnMain = criarBotao('btn-monitorar-performance', 'Parar monitoramento');
const btnFinalizar = criarBotao('btn-finalizar-monitoramento', 'Finalizar monitoramento');
const btnRequisicoesDetalhadas = criarBotao('btn-req-detalhadas-performance', 'Requisições detalhadas');
const btnAnaliseDetalhada = criarBotao('btn-analise-detalhada-performance', 'Análise detalhada');
// Inicialmente escondidos
btnRequisicoesDetalhadas.style.display = 'none';
btnAnaliseDetalhada.style.display = 'none';
btnFinalizar.style.display = 'none';
// Botão de esconder/mostrar com ícone de olho (fora do container)
const btnToggle = document.createElement('button');
btnToggle.id = 'btn-toggle-performance';
btnToggle.innerHTML = `
<img
src="https://cdn-icons-png.flaticon.com/512/565/565655.png"
alt="esconder"
style="width:15px; height:15px; vertical-align:middle; filter: invert(1);"
/>
`;
Object.assign(btnToggle.style, {
width: '38px',
height: '38px',
background: '#23272f',
color: '#f3f4f6',
borderRadius: '50%',
border: BUTTON_BORDER,
fontSize: '20px',
fontWeight: 'bold',
cursor: 'pointer',
boxShadow: BUTTON_BOX_SHADOW,
position: 'fixed',
left: '5px',
bottom: '5px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: '10000',
});
btnToggle.onmouseenter = () => {
btnToggle.style.background = '#353a40';
btnToggle.style.color = '#fff';
};
btnToggle.onmouseleave = () => {
btnToggle.style.background = '#23272f';
btnToggle.style.color = '#f3f4f6';
};
let hidden = false;
btnToggle.onclick = () => {
hidden = !hidden;
if (hidden) {
container.style.display = 'none';
btnToggle.innerHTML = `
<img
src="https://cdn-icons-png.flaticon.com/512/159/159604.png"
alt="mostrar"
style="width:15px; height:15px; vertical-align:middle; filter: invert(1);"
/>
`;
}
else {
container.style.display = 'flex';
btnToggle.innerHTML = `
<img
src="https://cdn-icons-png.flaticon.com/512/565/565655.png"
alt="esconder"
style="width:15px; height:15px; vertical-align:middle; filter: invert(1);"
/>
`;
}
};
// Estado dos botões
let estado = 'monitorando';
const atualizarBotoes = (novoEstado) => {
estado = novoEstado;
switch (estado) {
case 'inativo':
btnMain.innerText = 'Iniciar monitoramento';
this.monitoramentoAtivo = false;
btnFinalizar.style.display = 'none';
btnRequisicoesDetalhadas.style.display = 'none';
btnAnaliseDetalhada.style.display = 'none';
break;
case 'monitorando':
btnMain.innerText = 'Parar monitoramento';
this.monitoramentoAtivo = true;
btnFinalizar.style.display = 'none';
btnRequisicoesDetalhadas.style.display = 'none';
btnAnaliseDetalhada.style.display = 'none';
break;
case 'relatorio':
btnMain.innerText = 'Gerar relatório';
this.monitoramentoAtivo = false;
btnFinalizar.style.display = 'none';
btnRequisicoesDetalhadas.style.display = 'block';
btnAnaliseDetalhada.style.display = 'block';
break;
case 'relatorio_gerado':
btnMain.innerText = 'Gerar relatório';
this.monitoramentoAtivo = false;
btnFinalizar.style.display = 'block';
btnRequisicoesDetalhadas.style.display = 'block';
btnAnaliseDetalhada.style.display = 'block';
break;
}
};
// Ações dos botões
btnMain.onclick = () => {
if (estado === 'inativo') {
this.resetPerformanceData();
this.showWelcomeMessage();
atualizarBotoes('monitorando');
}
else if (estado === 'monitorando') {
atualizarBotoes('relatorio');
}
else if (estado === 'relatorio' || estado === 'relatorio_gerado') {
this.showPerformanceSummary(false);
atualizarBotoes('relatorio_gerado');
}
};
btnFinalizar.onclick = () => {
this.resetPerformanceData();
atualizarBotoes('inativo');
};
// Apêndice
this.showWelcomeMessage();
container.appendChild(btnMain);
container.appendChild(btnRequisicoesDetalhadas);
container.appendChild(btnAnaliseDetalhada);
container.appendChild(btnFinalizar);
document.body.appendChild(container);
document.body.appendChild(btnToggle);
// Ação do botão de requisições detalhadas
btnRequisicoesDetalhadas.onclick = () => {
this.showHttpRequestsReport();
};
// Ação do botão de análise detalhada
btnAnaliseDetalhada.onclick = () => {
this.generateDetailedAnalysis();
};
}
}
/**
* 🏭 Factory function para criar instância do LogsPerformatico
* @param serviceName Nome do serviço (para logs personalizados)
* @returns Nova instância de LogsPerformatico
*/
function createPerformanceLogger(serviceName = 'Service') {
return new LogsPerformatico(serviceName);
}
const GlobalPerformanceLogger = createPerformanceLogger('GLOBAL');
const PerformanceMonitor = PerformanceMonitorFactory(GlobalPerformanceLogger);
function PerformanceMonitorFactory(globalLogger) {
return function PerformanceMonitor(options) {
return function (target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
let opName;
let customMethod;
let customThresholds;
if (typeof options === 'object' && options !== null) {
opName = options.name || propertyKey;
customMethod = options.method;
customThresholds = {
ok: options.ok,
lento: options.lento,
critico: options.critico,
};
}
else if (typeof options === 'function') {
opName = options.name || propertyKey;
customMethod = options;
}
else if (typeof options === 'string') {
opName = options;
}
else {
opName = propertyKey;
}
descriptor.value = function (...args) {
return __awaiter(this, void 0, void 0, function* () {
const instanceLogger = this
.performanceLogger;
const logger = instanceLogger || globalLogger;
if (logger) {
logger.startTimer(opName);
try {
const methodToCall = customMethod || originalMethod;
const result = yield methodToCall.apply(this, args);
return result;
}
finally {
logger.endTimer(opName, undefined, customThresholds);
}
}
else {
const methodToCall = customMethod || originalMethod;
return yield methodToCall.apply(this, args);
}
});
};
return descriptor;
};
};
}
class PerformanceMonitorInterceptor {
intercept(req, next) {
var _a;
// Ignora arquivos de assets
if (!req.url.startsWith('./assets')) {
// 🔹 Recupera dados da sessão
let tela = sessionStorage.getItem('idTela');
if (!tela) {
tela = 'SEL041';
sessionStorage.setItem('idTela', tela);
}
const cdUsuario = (_a = sessionStorage.getItem('cdUsuario')) !== null && _a !== void 0 ? _a : '';
// 🔹 Clona a requisição adicionando headers
const authReq = req.clone({
headers: req.headers
.set('tela_id', tela)
.set('tela', 'SEL041')
.set('user', cdUsuario),
});
const start = performance.now();
return next.handle(authReq).pipe(tap({
next: (event) => {
var _a, _b;
if (event instanceof HttpResponse) {
const tempo = performance.now() - start;
// ✅ Verificação segura do body e suas propriedades
const operationType = ((_a = authReq.body) === null || _a === void 0 ? void 0 : _a.operationType) || 'UNKNOWN';
const operationName = ((_b = authReq.body) === null || _b === void 0 ? void 0 : _b.name) || '';
console.log(`%c🟢 [HTTP] ${operationType} ${operationType === 'SELECT_FULL' ? '' : operationName} - ${tempo.toFixed(1)}ms`, 'color: #6cbbd8ff; font-weight: bold;');
GlobalPerformanceLogger.registerHttpRequest(authReq, tempo, false);
}
},
error: (error) => {
var _a, _b;
const tempo = performance.now() - start;
// ✅ Verificação segura do body e suas propriedades
const operationType = ((_a = authReq.body) === null || _a === void 0 ? void 0 : _a.operationType) || 'UNKNOWN';
const operationName = ((_b = authReq.body) === null || _b === void 0 ? void 0 : _b.name) || '';
console.groupCollapsed(`%c🔴 [HTTP] ${operationType} ${operationName} - ${tempo.toFixed(1)}ms`, 'color: #6cbbd8ff; font-weight: bold;');
console.error(error);
console.groupEnd();
GlobalPerformanceLogger.registerHttpRequest(authReq, tempo, true);
},
}));
}
// Requisição de asset, passa direto
return next.handle(req);
}
}
PerformanceMonitorInterceptor.decorators = [
{ type: Injectable }
];
/*
* Public API Surface of performance-monitor
*/
/**
* Generated bundle index. Do not edit.
*/
export { GlobalPerformanceLogger, PerformanceMonitor, PerformanceMonitorComponent, PerformanceMonitorInterceptor, PerformanceMonitorModule, PerformanceMonitorService, LogsPerformatico as ɵa, createPerformanceLogger as ɵb };
//# sourceMappingURL=performance-monitor-logger.js.map