update 22

This commit is contained in:
2026-01-07 01:14:51 +09:00
parent 57c3eea429
commit 66e8e21302
220 changed files with 2911 additions and 700 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.gradle/file-system.probe Normal file

Binary file not shown.

1
.idea/gradle.xml generated
View File

@@ -4,6 +4,7 @@
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="temurin-21" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

5
.idea/misc.xml generated
View File

@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="openjdk-25" project-jdk-type="JavaSDK">
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -5,12 +5,12 @@ plugins {
}
group = 'research'
version = '0.0.1-SNAPSHOT'
version = '1.0.0'
description = 'log-hunter'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
languageVersion = JavaLanguageVersion.of(21)
}
}

View File

@@ -0,0 +1 @@
research.loghunter.LogHunterApplication

View File

@@ -0,0 +1 @@
spring.application.name=log-hunter

View File

@@ -0,0 +1,34 @@
server:
port: 8080
spring:
application:
name: log-hunter
datasource:
url: jdbc:sqlite:./data/loghunter.db
driver-class-name: org.sqlite.JDBC
jpa:
database-platform: org.hibernate.community.dialect.SQLiteDialect
hibernate:
ddl-auto: update
show-sql: false
properties:
hibernate:
format_sql: true
# 정적 리소스 캐시 비활성화
web:
resources:
cache:
cachecontrol:
no-cache: true
no-store: true
# 앱 설정
app:
crypto:
key: ${LOGHUNTER_CRYPTO_KEY:LogHunterDefaultKey32Bytes!!}
export:
path: ./exports

View File

@@ -0,0 +1 @@
import"./index-09HB4Lmg.js";import{_ as e,a as s,h as r,G as n,i as o,t as c,n as d}from"./index-BB0X_WMV.js";const l={__name:"Badge",props:{text:String,variant:{type:String,default:"default"}},setup(a){return(t,i)=>(r(),s("span",{class:d(["badge",`badge-${a.variant}`])},[n(t.$slots,"default",{},()=>[o(c(a.text),1)])],2))}},g=e(l,[["__scopeId","data-v-b7bd2350"]]);export{g as B};

View File

@@ -0,0 +1 @@
import"./index-09HB4Lmg.js";import{_ as d,r,a,e as c,G as u,n as f,h as o}from"./index-BB0X_WMV.js";const m=["type","disabled"],b={key:0,class:"spinner"},y={__name:"Button",props:{type:{type:String,default:"button"},variant:{type:String,default:"primary"},size:{type:String,default:"md"},disabled:Boolean,loading:Boolean},emits:["click"],setup(e,{expose:l}){const n=r(null);return l({focus:()=>{var t;(t=n.value)==null||t.focus()}}),(t,s)=>(o(),a("button",{ref_key:"buttonRef",ref:n,type:e.type,class:f(["btn",`btn-${e.variant}`,{"btn-sm":e.size==="sm","btn-lg":e.size==="lg"}]),disabled:e.disabled||e.loading,onClick:s[0]||(s[0]=i=>t.$emit("click",i))},[e.loading?(o(),a("span",b)):c("",!0),u(t.$slots,"default",{},void 0)],10,m))}},B=d(y,[["__scopeId","data-v-c92354e1"]]);export{B};

View File

@@ -0,0 +1 @@
import{_ as C,r as g,o as x,a as l,b as s,l as w,s as I,t as d,F as B,g as T,h as o,d as R,w as m,f as L,u as _}from"./index-BB0X_WMV.js";import{a as A,C as F}from"./index-09HB4Lmg.js";import{C as O,a as W,L as N,B as V,p as E,b as M,c as $,d as P,e as z}from"./chartjs-plugin-datalabels.esm-DiDzp_cw.js";const U={class:"daily-stats"},Y={class:"page-header"},Z={class:"filter-section"},j={key:0,class:"loading"},q={key:1,class:"no-data"},G={key:2,class:"server-charts"},H={class:"chart-header"},J={class:"chart-subtitle"},K={class:"chart-wrapper"},Q={class:"chart-container"},X={__name:"DailyStats",setup(tt){O.register(W,N,V,E,M,$,P);const c=g(!1),r=g([]),n=g(f());function f(){return new Date().toISOString().split("T")[0]}function b(){const t=new Date(n.value);t.setDate(t.getDate()-1),n.value=t.toISOString().split("T")[0],i()}function y(){const t=new Date(n.value);t.setDate(t.getDate()+1),n.value=t.toISOString().split("T")[0],i()}const S={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{position:"top"},tooltip:{mode:"index",intersect:!1},datalabels:{display:t=>{if(t.datasetIndex!==2)return!1;const e=t.chart.data.datasets,a=t.dataIndex;return e.reduce((u,p)=>u+(p.data[a]||0),0)>0},anchor:"end",align:"end",offset:0,font:{size:9},color:"#666",formatter:(t,e)=>{const a=e.chart.data.datasets,v=e.dataIndex;return a.reduce((u,p)=>u+(p.data[v]||0),0)}}},scales:{x:{stacked:!0,ticks:{maxRotation:0,autoSkip:!0,maxTicksLimit:24,callback:function(t,e){const a=this.getLabelForValue(t);return a&&a.endsWith(":00")?a:""}},grid:{display:!1}},y:{stacked:!0,beginAtZero:!0}},barPercentage:.8,categoryPercentage:.9},D=t=>({labels:t.timeStats.map(a=>a.time),datasets:[{label:"CRITICAL",data:t.timeStats.map(a=>a.critical),backgroundColor:"#9b59b6",borderWidth:0},{label:"ERROR",data:t.timeStats.map(a=>a.error),backgroundColor:"#e74c3c",borderWidth:0},{label:"WARN",data:t.timeStats.map(a=>a.warn),backgroundColor:"#f39c12",borderWidth:0}]}),i=async()=>{c.value=!0;try{r.value=await A.getTimeStatsByServer(n.value,15)}catch(t){console.error("Failed to load stats:",t),r.value=[]}finally{c.value=!1}},h=t=>{const e=new Date(t);return`${e.getFullYear()}${e.getMonth()+1}${e.getDate()}`},k=t=>t.timeStats.reduce((e,a)=>e+a.total,0);return x(()=>{i()}),(t,e)=>(o(),l("div",U,[s("div",Y,[e[1]||(e[1]=s("h2",null,"일별 에러현황",-1)),s("div",Z,[s("button",{class:"nav-btn",onClick:b},"◀ 이전"),w(s("input",{type:"date","onUpdate:modelValue":e[0]||(e[0]=a=>n.value=a),onChange:i},null,544),[[I,n.value]]),s("button",{class:"nav-btn",onClick:y},"다음 ▶")])]),c.value?(o(),l("div",j,[...e[2]||(e[2]=[s("p",null,"로딩중...",-1)])])):r.value.length===0?(o(),l("div",q,[s("p",null,d(h(n.value))+"에 분석된 에러 데이터가 없습니다.",1)])):(o(),l("div",G,[(o(!0),l(B,null,T(r.value,a=>(o(),R(_(F),{key:a.serverId,class:"server-chart-card"},{header:m(()=>[s("div",H,[s("h3",null,"🖥️ "+d(a.serverName),1),s("span",J,d(h(n.value))+" 15분 단위 에러 ("+d(k(a))+"건)",1)])]),default:m(()=>[s("div",K,[s("div",Q,[L(_(z),{data:D(a),options:S},null,8,["data"])])])]),_:2},1024))),128))]))]))}},nt=C(X,[["__scopeId","data-v-b7241be8"]]);export{nt as default};

View File

@@ -0,0 +1 @@
.page-header[data-v-b7241be8]{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px}.page-header h2[data-v-b7241be8]{margin:0}.filter-section[data-v-b7241be8]{display:flex;align-items:center;gap:8px}.filter-section input[type=date][data-v-b7241be8]{padding:10px 16px;border:1px solid #ddd;border-radius:6px;font-size:14px;cursor:pointer}.filter-section input[type=date][data-v-b7241be8]:focus{outline:none;border-color:#3498db}.nav-btn[data-v-b7241be8]{padding:10px 16px;border:1px solid #ddd;border-radius:6px;background:#fff;cursor:pointer;font-size:14px;transition:all .2s}.nav-btn[data-v-b7241be8]:hover{background:#f0f0f0;border-color:#3498db}.loading[data-v-b7241be8],.no-data[data-v-b7241be8]{text-align:center;padding:60px;color:#666;background:#fff;border-radius:8px}.server-charts[data-v-b7241be8]{display:flex;flex-direction:column;gap:20px}.server-chart-card[data-v-b7241be8]{width:100%}.chart-header[data-v-b7241be8]{display:flex;align-items:center;gap:12px}.chart-header h3[data-v-b7241be8]{margin:0;font-size:16px}.chart-subtitle[data-v-b7241be8]{font-size:13px;color:#888}.chart-wrapper[data-v-b7241be8]{overflow-x:auto}.chart-container[data-v-b7241be8]{height:220px;min-width:100%;padding:8px 0}

View File

@@ -0,0 +1 @@
.dashboard-header[data-v-abd43acf]{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px}.dashboard-header h2[data-v-abd43acf]{margin:0}.server-grid[data-v-abd43acf]{display:grid;grid-template-columns:repeat(auto-fill,minmax(350px,1fr));gap:20px;margin-bottom:24px}.server-card[data-v-abd43acf]{transition:box-shadow .2s}.server-card[data-v-abd43acf]:hover{box-shadow:0 4px 12px #00000026}.server-header[data-v-abd43acf]{display:flex;justify-content:space-between;align-items:center}.server-title[data-v-abd43acf]{display:flex;align-items:center;gap:10px}.server-title h4[data-v-abd43acf]{margin:0;font-size:16px}.server-info[data-v-abd43acf]{margin-bottom:12px}.info-row[data-v-abd43acf]{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid #f0f0f0}.info-row[data-v-abd43acf]:last-child{border-bottom:none}.info-row .label[data-v-abd43acf]{color:#666;font-size:13px}.info-row .value[data-v-abd43acf]{font-weight:500}.info-row .value.has-error[data-v-abd43acf]{color:#e74c3c}.progress-section[data-v-abd43acf]{padding:12px;background:#f8f9fa;border-radius:8px;margin-top:12px}.progress-header[data-v-abd43acf]{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.status-text[data-v-abd43acf]{font-size:13px;color:#333}.progress-bar-container[data-v-abd43acf]{height:8px;background:#e0e0e0;border-radius:4px;overflow:hidden;margin-bottom:8px}.progress-bar[data-v-abd43acf]{height:100%;background:#3498db;transition:width .3s}.progress-details[data-v-abd43acf]{display:flex;justify-content:space-between;font-size:12px;color:#666}.empty-card[data-v-abd43acf]{text-align:center}.empty-content[data-v-abd43acf]{padding:40px 20px}.empty-content p[data-v-abd43acf]{margin-bottom:16px;color:#666}.daily-charts[data-v-abd43acf]{margin-top:32px}.daily-charts h3[data-v-abd43acf]{margin-bottom:16px;font-size:18px}.chart-list[data-v-abd43acf]{display:flex;flex-direction:column;gap:16px}.chart-card[data-v-abd43acf]{width:100%}.chart-header[data-v-abd43acf]{display:flex;justify-content:space-between;align-items:center}.chart-total[data-v-abd43acf]{font-size:13px;color:#888}.chart-container[data-v-abd43acf]{height:200px;padding:8px 0}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import"./index-jV6SX453.js";import{_ as h,a as e,i as a,b as o,e as u,F as d,g as i,k as f,t as c,s as y,j as g}from"./index-CZ3IEKgR.js";const b={class:"data-table-wrapper"},p={class:"data-table"},$={key:0,class:"actions-col"},_={key:0},D=["colspan"],S={key:1},T=["colspan"],B=["onClick"],N={key:0,class:"actions-col"},V={__name:"DataTable",props:{columns:{type:Array,required:!0},data:{type:Array,default:()=>[]},loading:{type:Boolean,default:!1},emptyText:{type:String,default:"데이터가 없습니다."}},emits:["row-click"],setup(n){const k=(t,r)=>t==null?"-":r.type==="date"&&t?new Date(t).toLocaleString("ko-KR"):r.type==="boolean"?t?"Y":"N":t;return(t,r)=>(a(),e("div",b,[o("table",p,[o("thead",null,[o("tr",null,[(a(!0),e(d,null,i(n.columns,s=>(a(),e("th",{key:s.key,style:f({width:s.width})},c(s.label),5))),128)),t.$slots.actions?(a(),e("th",$,"작업")):u("",!0)])]),o("tbody",null,[n.loading?(a(),e("tr",_,[o("td",{colspan:n.columns.length+(t.$slots.actions?1:0),class:"loading-cell"}," 로딩 중... ",8,D)])):!n.data||n.data.length===0?(a(),e("tr",S,[o("td",{colspan:n.columns.length+(t.$slots.actions?1:0),class:"empty-cell"},c(n.emptyText),9,T)])):(a(!0),e(d,{key:2},i(n.data,(s,m)=>(a(),e("tr",{key:s.id||m,onClick:l=>t.$emit("row-click",s)},[(a(!0),e(d,null,i(n.columns,l=>(a(),e("td",{key:l.key},[y(t.$slots,l.key,{row:s,value:s[l.key]},()=>[g(c(k(s[l.key],l)),1)])]))),128)),t.$slots.actions?(a(),e("td",N,[y(t.$slots,"actions",{row:s},void 0)])):u("",!0)],8,B))),128))])])]))}},A=h(V,[["__scopeId","data-v-db5e24a9"]]);export{A as D};
import"./index-09HB4Lmg.js";import{_ as h,a as e,h as a,b as o,e as u,F as d,g as i,j as f,t as c,G as y,i as g}from"./index-BB0X_WMV.js";const b={class:"data-table-wrapper"},p={class:"data-table"},$={key:0,class:"actions-col"},_={key:0},D=["colspan"],S={key:1},T=["colspan"],B=["onClick"],N={key:0,class:"actions-col"},V={__name:"DataTable",props:{columns:{type:Array,required:!0},data:{type:Array,default:()=>[]},loading:{type:Boolean,default:!1},emptyText:{type:String,default:"데이터가 없습니다."}},emits:["row-click"],setup(n){const m=(t,r)=>t==null?"-":r.type==="date"&&t?new Date(t).toLocaleString("ko-KR"):r.type==="boolean"?t?"Y":"N":t;return(t,r)=>(a(),e("div",b,[o("table",p,[o("thead",null,[o("tr",null,[(a(!0),e(d,null,i(n.columns,s=>(a(),e("th",{key:s.key,style:f({width:s.width})},c(s.label),5))),128)),t.$slots.actions?(a(),e("th",$,"작업")):u("",!0)])]),o("tbody",null,[n.loading?(a(),e("tr",_,[o("td",{colspan:n.columns.length+(t.$slots.actions?1:0),class:"loading-cell"}," 로딩 중... ",8,D)])):!n.data||n.data.length===0?(a(),e("tr",S,[o("td",{colspan:n.columns.length+(t.$slots.actions?1:0),class:"empty-cell"},c(n.emptyText),9,T)])):(a(!0),e(d,{key:2},i(n.data,(s,k)=>(a(),e("tr",{key:s.id||k,onClick:l=>t.$emit("row-click",s)},[(a(!0),e(d,null,i(n.columns,l=>(a(),e("td",{key:l.key},[y(t.$slots,l.key,{row:s,value:s[l.key]},()=>[g(c(m(s[l.key],l)),1)])]))),128)),t.$slots.actions?(a(),e("td",N,[y(t.$slots,"actions",{row:s},void 0)])):u("",!0)],8,B))),128))])])]))}},A=h(V,[["__scopeId","data-v-db5e24a9"]]);export{A as D};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More