22a-page-header( title ="Pipeline Configuration" : show- back= "false" )
33
44a-alert Pipeline is a mechanism in GreptimeDB for parsing and transforming log data, <a href =" https://docs.greptime.com/user-guide/logs/pipeline-config" target =" _blank" >read more</a >
5- a-layout( style ="box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.08)" )
6- a-layout-sider( :resize-directions ="['right']" : width= "650 " )
5+ a-layout.full-height-layout ( style ="box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.08)" )
6+ a-layout-sider( :resize-directions ="['right']" : width= "800 " )
77 a-card( title ="Pipeline" : bordered= "false" )
88 template( #extra )
99 a-space
@@ -25,52 +25,58 @@ a-layout(style="box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.08)")
2525 :disabled ="!isCreating"
2626 :rules ="rules"
2727 )
28+ .form-description Input the pipeline configuration here to define how logs are parsed and transformed, You can test on the right side whether it's already saved.
2829 a-form-item( field ="name" label ="Pipeline name" style ="width: 200px" )
2930 a-input( v-model ="currFile.name" placeholder ="Pipeline name" )
3031 a-form-item( v-if ="!isCreating" field ="version" label ="Version" )
3132 | {{ currFile.version }}
3233 a-form-item( field ="content" label ="Yaml Content" )
3334 template( #help )
3435 div
35- YMLEditorSimple( v-model ="currFile.content" style ="width: 100%; height: 525px" )
36+ .editor-container
37+ YMLEditorSimple( v-model ="currFile.content" style ="width: 100%; height: calc(-470px + 100vh)" )
3638 a-layout-content
37- div ( style = "display: flex; flex-direction: column" )
38- a-card.light-editor-card ( title ="Input" style = "flex: 1" : bordered= "false" )
39+ .content-container
40+ a-card.light-editor-card ( title ="Input" : bordered= "false" )
3941 template( #extra )
4042 a-space
4143 a-button( size ="small" @click ="handleDebug" ) Test
42- //- a(href="https://github.com/GreptimeTeam/demo-scene/tree/main/vector-ingestion" target="_blank") Write Log Demo
43-
44+ a-select( v-model ="selectedContentType" style ="width: 150px" placeholder ="Content Type" )
45+ a-option( value ="text/plain" ) text
46+ a-option( value ="application/json" ) json
47+ a-option( value ="application/x-ndjson" ) ndjson
4448 .right-content
4549 a-alert( v-if ="ymlError" type ="error" )
4650 | {{ ymlError }}
4751 a-typography-text( type ="secondary" )
4852 | Input your original log to see parse results.
49- CodeMirror(
50- v-model ="debugForm.content"
51- style ="height: 320px; width: 100%; margin-top: 5px"
52- :extensions ="extensions"
53- :spellcheck ="true"
54- :autofocus ="true"
55- :indent-with-tab ="true"
56- :tabSize ="2"
57- :placeholder ="debugTip"
58- )
59-
60- a-card.light-editor-card ( title ="Output" style ="flex: 1" : bordered= "false" )
53+ .input-editor
54+ CodeMirror(
55+ v-model ="debugForm.content"
56+ style ="width: 100%; height: 100%"
57+ :extensions ="extensions"
58+ :spellcheck ="true"
59+ :autofocus ="true"
60+ :indent-with-tab ="true"
61+ :tabSize ="2"
62+ :placeholder ="debugTip"
63+ )
64+
65+ a-card.light-editor-card ( title ="Output" : bordered= "false" )
6166 .right-content
6267 a-typography-text( type ="secondary" )
6368 | Parsed logs displayed here. Logs that ingested via API will follow this structure.
64- CodeMirror(
65- style ="height: 340px; width: 100%; margin-top: 5px"
66- :model-value ="debugResponse"
67- :extensions ="extensions"
68- :spellcheck ="true"
69- :autofocus ="true"
70- :indent-with-tab ="true"
71- :tabSize ="2"
72- :disabled ="true"
73- )
69+ .output-editor
70+ CodeMirror(
71+ style ="width: 100%; height: 100%"
72+ :model-value ="debugResponse"
73+ :extensions ="extensions"
74+ :spellcheck ="true"
75+ :autofocus ="true"
76+ :indent-with-tab ="true"
77+ :tabSize ="2"
78+ :disabled ="true"
79+ )
7480</template >
7581
7682<script setup name="PipeFileView" lang="ts">
@@ -86,7 +92,6 @@ a-layout(style="box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.08)")
8692 filename: undefined | string
8793 }>()
8894
89- const debugTip = ' Input raw Strings or JSON Object Array'
9095 const currFile = reactive <PipeFile >({
9196 name: ' ' ,
9297 content: ` processors:
@@ -171,19 +176,42 @@ transform:
171176 ' 210.207.142.115 - AnthraX [26/Dec/2024:16:47:19 +0800] "DELETE /do-not-access/needs-work HTTP/2.0" 200 4488' ,
172177 })
173178
179+ const selectedContentType = ref (' text/plain' )
180+ // Watch for content changes to auto-detect content type
181+ watch (
182+ () => debugForm .content ,
183+ (newContent ) => {
184+ try {
185+ JSON .parse (newContent )
186+ selectedContentType .value = ' application/json'
187+ } catch (e ) {
188+ // If content contains newlines and each line is valid JSON, it's NDJSON
189+ if (newContent .includes (' \n ' )) {
190+ const lines = newContent .split (' \n ' ).filter ((line ) => line .trim ())
191+ const isNDJSON = lines .every ((line ) => {
192+ try {
193+ JSON .parse (line )
194+ return true
195+ } catch {
196+ return false
197+ }
198+ })
199+ if (isNDJSON ) {
200+ selectedContentType .value = ' application/x-ndjson'
201+ } else {
202+ selectedContentType .value = ' text/plain'
203+ }
204+ } else {
205+ selectedContentType .value = ' text/plain'
206+ }
207+ }
208+ }
209+ )
210+
174211 const extensions = [basicSetup , json ()]
175212 const debugResponse = ref (' ' )
176213 function handleDebug() {
177- let content
178- try {
179- content = JSON .parse (debugForm .content )
180- if (! Array .isArray (content )) {
181- content = [content ]
182- }
183- } catch (e ) {
184- content = debugForm .content .split (' \n ' )
185- }
186- debugContent (currFile .content , content ).then ((result ) => {
214+ debugContent (currFile .content , debugForm .content , selectedContentType .value ).then ((result ) => {
187215 debugResponse .value = JSON .stringify (result , null , 2 )
188216 })
189217 }
@@ -208,11 +236,101 @@ transform:
208236 }
209237 .right-content {
210238 padding : 0 10px 10px 10px ;
239+ height : 100% ;
240+ display : flex ;
241+ flex-direction : column ;
211242 }
212243 :deep(.arco-card.light-editor-card ) {
213244 padding-right : 0 ;
245+ flex : 1 ;
246+ display : flex ;
247+ flex-direction : column ;
248+ min-height : 0 ;
214249 }
215250 :deep(.arco-layout-sider-light ) {
216251 box-shadow : none ;
217252 }
253+ :deep(.arco-form-item-content-flex ) {
254+ display : block ;
255+ }
256+
257+ .content-container {
258+ height : 100% ;
259+ display : flex ;
260+ flex-direction : column ;
261+ gap : 16px ;
262+ padding-bottom : 16px ;
263+ }
264+
265+ .input-editor ,
266+ .output-editor {
267+ flex : 1 ;
268+ min-height : 0 ;
269+ margin-top : 5px ;
270+
271+ :deep(.cm-editor ) {
272+ height : 100% ;
273+ }
274+ }
275+
276+ .output-editor {
277+ :deep(.cm-editor ) {
278+ background-color : var (--color-fill-2 );
279+ cursor : not-allowed ;
280+ }
281+
282+ :deep(.cm-content ) {
283+ color : var (--color-text-2 );
284+ }
285+ }
286+
287+ .form-description {
288+ color : var (--color-text-3 );
289+ font-size : 14px ;
290+ margin-bottom : 16px ;
291+ }
292+
293+ .full-height-layout {
294+ height : calc (100vh - 133px ); // Subtract header height and alert height
295+
296+ :deep(.arco-layout ) {
297+ height : 100% ;
298+ }
299+
300+ :deep(.arco-layout-content ) {
301+ height : 100% ;
302+ overflow : auto ;
303+ }
304+
305+ :deep(.arco-layout-sider ) {
306+ height : 100% ;
307+ overflow : auto ;
308+ overflow-x : hidden ; // Prevent horizontal scrollbar
309+ }
310+
311+ :deep(.arco-card-body ) {
312+ padding : 0 ; // Remove default padding that might cause overflow
313+ height : 100% ;
314+ }
315+ }
316+
317+ .editor-container {
318+ min-height : 300px ; // Set a minimum height
319+ }
320+
321+ // Add styles for editor borders
322+ :deep(.cm-editor ) {
323+ border : 1px solid var (--color-border );
324+ border-radius : 4px ;
325+ }
326+
327+ :deep(.editor-container ) {
328+ .cm-editor {
329+ border : 1px solid var (--color-border );
330+ border-radius : 4px ;
331+ }
332+ }
333+ :deep(.cm-editor.cm-focused ) {
334+ outline : 0 ;
335+ }
218336 </style >
0 commit comments