Skip to content

Commit 3f25012

Browse files
committed
feat: models + fix logs + decorators + wip
1 parent 622f61e commit 3f25012

File tree

13 files changed

+899
-100
lines changed

13 files changed

+899
-100
lines changed

.yarn/patches/@kubernetes-models-crd-generate-npm-5.0.2-d0c40558b7.patch

Lines changed: 392 additions & 0 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"@eslint/compat": "^1.2.7",
2929
"@eslint/eslintrc": "^3.3.1",
3030
"@eslint/js": "^9.23.0",
31-
"@kubernetes-models/crd-generate": "^5.0.2",
31+
"@kubernetes-models/crd-generate": "patch:@kubernetes-models/crd-generate@npm%3A5.0.2#~/.yarn/patches/@kubernetes-models-crd-generate-npm-5.0.2-d0c40558b7.patch",
3232
"@types/chai": "^5.2.0",
3333
"@types/fs-extra": "^11",
3434
"@types/js-yaml": "^4",

packages/cli/src/commands/compo/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ async function bundleTypeScript(
9090
minify: false,
9191
keepNames: true,
9292
legalComments: "inline",
93+
// Enable TypeScript decorators
94+
tsconfigRaw: {
95+
compilerOptions: {
96+
experimentalDecorators: true,
97+
},
98+
},
9399
}
94100

95101
// If embedDeps is false, add plugin to keep dependencies external

packages/server/src/executor.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createLogger } from "@crossplane-js/libs"
22

3+
import { createModel } from "./model.ts"
34
import type { NodeResponse, NodeError, FunctionInput } from "./types.ts"
4-
// import { createModel } from "./model.ts"
55

66
// Create a logger for this module
77
const moduleLogger = createLogger("executor")
@@ -61,11 +61,9 @@ export async function executeCode(
6161
try {
6262
const inputData = input as any
6363

64-
result = await module.default(inputData)
65-
// WIP
66-
// const compositeResource = inputData?.observed?.composite?.resource
67-
// const composite = createModel(compositeResource)
68-
// result = await module.default(composite)
64+
const compositeResource = inputData?.observed?.composite?.resource
65+
const composite = createModel(compositeResource)
66+
result = await module.default(composite)
6967

7068
moduleLogger.debug("Function execution completed")
7169
} catch (execErr) {

packages/server/src/server.ts

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,40 +38,63 @@ export function createServer(port: number, codeFilePath: string) {
3838
try {
3939
const { input } = req.body as NodeRequest
4040

41-
// Enhanced logging of the full request body
42-
moduleLogger.debug({ body: req.body }, "=== REQUEST RECEIVED ===")
41+
// Log request metadata without sensitive content
42+
moduleLogger.debug("=== REQUEST RECEIVED ===")
43+
moduleLogger.debug(`Request method: ${req.method}, path: ${req.path}`)
44+
moduleLogger.debug(`Input data size: ${JSON.stringify(input).length} bytes`)
4345

44-
// Specifically log observed resources if present
46+
// Specifically log observed resources if present (without full content)
4547
if (req.body.observed) {
4648
moduleLogger.debug("=== OBSERVED RESOURCES ===")
4749

48-
// Log composite resource if present
50+
// Log composite resource metadata if present
4951
if (req.body.observed.composite) {
50-
moduleLogger.debug({ composite: req.body.observed.composite }, "Composite Resource:")
52+
const composite = req.body.observed.composite
53+
moduleLogger.debug(
54+
{
55+
compositeKind: composite.resource?.kind,
56+
compositeName: composite.resource?.metadata?.name,
57+
compositeNamespace: composite.resource?.metadata?.namespace,
58+
},
59+
"Composite Resource metadata"
60+
)
5161
}
5262

53-
// Log individual resources if present
63+
// Log individual resources metadata if present
5464
if (req.body.observed.resources) {
55-
moduleLogger.info("Resources:")
5665
const resourceNames = Object.keys(req.body.observed.resources)
5766
moduleLogger.info(`Found ${resourceNames.length} resources: ${resourceNames.join(", ")}`)
5867

59-
// Log each resource
68+
// Log each resource metadata (not full content)
6069
for (const [name, resource] of Object.entries(req.body.observed.resources)) {
61-
moduleLogger.info({ resource }, `Resource "${name}"`)
70+
const resourceObj = resource as any
71+
moduleLogger.info(
72+
{
73+
resourceName: name,
74+
kind: resourceObj.resource?.kind,
75+
name: resourceObj.resource?.metadata?.name,
76+
namespace: resourceObj.resource?.metadata?.namespace,
77+
},
78+
`Resource "${name}" metadata`
79+
)
6280
}
6381
}
6482
}
6583

6684
moduleLogger.info("=== EXECUTING CODE ===")
67-
moduleLogger.info(`Input length: ${JSON.stringify(input).length}`)
6885

6986
const result = await executeCode(codeFilePath, input)
7087

7188
moduleLogger.info("=== CODE EXECUTION COMPLETED ===")
7289

73-
// Log the response for debugging
74-
moduleLogger.debug(`Execute response: ${JSON.stringify(result, null, 2)}`)
90+
// Log response metadata without full content
91+
if (result.error) {
92+
moduleLogger.debug(`Execute response: error - ${result.error.message}`)
93+
} else {
94+
moduleLogger.debug(
95+
`Execute response: success - result size: ${JSON.stringify(result.result || {}).length} bytes`
96+
)
97+
}
7598

7699
res.json(result)
77100
} catch (err: unknown) {

pkg/node/logwriter.go

Lines changed: 92 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
type logWriter struct {
1313
logger logger.Logger
1414
prefix string
15+
streamType string // "stdout" or "stderr"
1516
buffer []byte
1617
bufferLock sync.Mutex
1718
}
@@ -29,49 +30,103 @@ func (w *logWriter) Write(p []byte) (n int, err error) {
2930
// Log each complete line
3031
for _, line := range lines {
3132
if line != "" {
32-
// Try to parse the line as JSON
33-
var jsonData map[string]interface{}
34-
if err := json.Unmarshal([]byte(line), &jsonData); err == nil {
35-
// Successfully parsed as JSON
36-
// Create a new map with "js-" prefix for each key
37-
prefixedData := make(map[string]interface{})
38-
for k, v := range jsonData {
39-
prefixedData["js-"+k] = v
40-
}
41-
42-
// Check if this is an error message
43-
if _, hasError := jsonData["error"]; hasError {
44-
// Log as an error with fields
45-
w.logger.WithFields(prefixedData).
46-
WithField(logger.FieldComponent, "node").
47-
Error("Node.js process reported an error")
48-
} else {
49-
// Log as info with fields
50-
w.logger.WithFields(prefixedData).
51-
WithField(logger.FieldComponent, "node").
52-
Info(w.prefix)
53-
}
54-
} else {
55-
// Check if this looks like an error message
56-
if strings.Contains(strings.ToLower(line), "error") ||
57-
strings.Contains(strings.ToLower(line), "exception") ||
58-
strings.Contains(strings.ToLower(line), "fail") {
59-
// Log as an error
60-
w.logger.WithField(logger.FieldComponent, "node").
61-
WithField(logger.FieldError, line).
62-
Error("Node.js process reported an error")
63-
} else {
64-
// Log as plain text
65-
w.logger.WithField(logger.FieldComponent, "node").
66-
Infof("%s%s", w.prefix, line)
67-
}
68-
}
33+
w.logLine(line)
6934
}
7035
}
7136

7237
return len(p), nil
7338
}
7439

40+
// logLine processes and logs a single line
41+
func (w *logWriter) logLine(line string) {
42+
// Add stream type to logger context
43+
contextLogger := w.logger.WithField(logger.FieldComponent, "node").
44+
WithField("stream", w.streamType)
45+
46+
// Try to parse the line as JSON (Pino format)
47+
var jsonData map[string]interface{}
48+
if err := json.Unmarshal([]byte(line), &jsonData); err == nil {
49+
// Successfully parsed as JSON - this is likely Pino output
50+
w.logPinoMessage(contextLogger, jsonData)
51+
} else {
52+
// Not JSON - handle as plain text
53+
w.logPlainText(contextLogger, line)
54+
}
55+
}
56+
57+
// logPinoMessage handles structured Pino log messages
58+
func (w *logWriter) logPinoMessage(contextLogger logger.Logger, jsonData map[string]interface{}) {
59+
// Create a new map with "js-" prefix for each key to avoid conflicts
60+
prefixedData := make(map[string]interface{})
61+
for k, v := range jsonData {
62+
prefixedData["js-"+k] = v
63+
}
64+
65+
// Extract log level from Pino message
66+
var logLevel string
67+
if level, ok := jsonData["level"].(string); ok {
68+
logLevel = strings.ToUpper(level)
69+
} else if levelNum, ok := jsonData["level"].(float64); ok {
70+
// Pino uses numeric levels: 10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal
71+
switch int(levelNum) {
72+
case 10:
73+
logLevel = "TRACE"
74+
case 20:
75+
logLevel = "DEBUG"
76+
case 30:
77+
logLevel = "INFO"
78+
case 40:
79+
logLevel = "WARN"
80+
case 50:
81+
logLevel = "ERROR"
82+
case 60:
83+
logLevel = "FATAL"
84+
default:
85+
logLevel = "INFO"
86+
}
87+
}
88+
89+
// Extract message
90+
var message string
91+
if msg, ok := jsonData["msg"].(string); ok {
92+
message = msg
93+
} else {
94+
message = w.prefix
95+
}
96+
97+
// Log based on the original level and stream type
98+
loggerWithFields := contextLogger.WithFields(prefixedData)
99+
100+
// For stderr or error levels, log as error
101+
if w.streamType == "stderr" || logLevel == "ERROR" || logLevel == "FATAL" {
102+
loggerWithFields.Error(message)
103+
} else if logLevel == "WARN" {
104+
loggerWithFields.Warn(message)
105+
} else if logLevel == "DEBUG" || logLevel == "TRACE" {
106+
loggerWithFields.Debug(message)
107+
} else {
108+
// Default to info for stdout and info level
109+
loggerWithFields.Info(message)
110+
}
111+
}
112+
113+
// logPlainText handles unstructured text messages
114+
func (w *logWriter) logPlainText(contextLogger logger.Logger, line string) {
115+
// Check if this looks like an error message or if it's from stderr
116+
isError := w.streamType == "stderr" ||
117+
strings.Contains(strings.ToLower(line), "error") ||
118+
strings.Contains(strings.ToLower(line), "exception") ||
119+
strings.Contains(strings.ToLower(line), "fail") ||
120+
strings.Contains(strings.ToLower(line), "fatal")
121+
122+
if isError {
123+
contextLogger.WithField(logger.FieldError, line).
124+
Error("Node.js process output")
125+
} else {
126+
contextLogger.Infof("%s%s", w.prefix, line)
127+
}
128+
}
129+
75130
// processBuffer processes the buffer and returns complete lines
76131
// Any incomplete line at the end remains in the buffer
77132
func (w *logWriter) processBuffer() []string {

pkg/node/process.go

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -436,19 +436,18 @@ func (pm *ProcessManager) getOrCreateProcess(ctx context.Context, input *types.X
436436
procLogger.Info("Running yarn install")
437437
yarnCmd := exec.Command("yarn", "install")
438438
yarnCmd.Dir = uniqueDirPath // Set the working directory
439-
yarnCmd.Env = append(os.Environ(),
440-
"NODE_OPTIONS=--experimental-strip-types --experimental-transform-types --no-warnings",
441-
"NODE_NO_WARNINGS=1",
442-
)
439+
yarnCmd.Env = os.Environ()
443440

444441
// Create a custom logWriter for yarn output
445442
yarnStdoutWriter := &logWriter{
446-
logger: procLogger.WithField("yarn", "stdout"),
447-
prefix: fmt.Sprintf("yarn[%s]: ", specHash[:8]),
443+
logger: procLogger.WithField("yarn", "stdout"),
444+
prefix: fmt.Sprintf("yarn[%s]: ", specHash[:8]),
445+
streamType: "stdout",
448446
}
449447
yarnStderrWriter := &logWriter{
450-
logger: procLogger.WithField("yarn", "stderr"),
451-
prefix: fmt.Sprintf("yarn[%s]: ", specHash[:8]),
448+
logger: procLogger.WithField("yarn", "stderr"),
449+
prefix: fmt.Sprintf("yarn[%s]: ", specHash[:8]),
450+
streamType: "stderr",
452451
}
453452

454453
yarnCmd.Stdout = yarnStdoutWriter
@@ -474,17 +473,24 @@ func (pm *ProcessManager) getOrCreateProcess(ctx context.Context, input *types.X
474473

475474
cmd.Env = append(os.Environ(),
476475
fmt.Sprintf("PORT=%d", port),
477-
"NODE_OPTIONS=--experimental-strip-types --experimental-transform-types --no-warnings",
478-
"NODE_NO_WARNINGS=1",
476+
"XFUNCJS_LOG_LEVEL=debug", // Ensure we capture all logs from Node.js
477+
"LOG_LEVEL=debug", // Fallback for Pino logger
479478
)
480479

481-
// Create a custom logWriter
480+
// Create custom logWriters for both stdout and stderr
481+
stdoutWriter := &logWriter{
482+
logger: procLogger,
483+
prefix: fmt.Sprintf("node[%s]: ", specHash[:8]),
484+
streamType: "stdout",
485+
}
482486
stderrWriter := &logWriter{
483-
logger: procLogger,
484-
prefix: fmt.Sprintf("node[%s]: ", specHash[:8]),
487+
logger: procLogger,
488+
prefix: fmt.Sprintf("node[%s]: ", specHash[:8]),
489+
streamType: "stderr",
485490
}
486491

487-
// Redirect stderr to our logger
492+
// Redirect both stdout and stderr to our loggers
493+
cmd.Stdout = stdoutWriter
488494
cmd.Stderr = stderrWriter
489495

490496
// Start the process
11.2 KB
Binary file not shown.

0 commit comments

Comments
 (0)