Skip to content

Commit fcff062

Browse files
authored
chore: Increasing telemetry of config parsing (#5309)
1 parent a907cd4 commit fcff062

4 files changed

Lines changed: 493 additions & 94 deletions

File tree

config/config.go

Lines changed: 101 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"github.com/gruntwork-io/terragrunt/internal/experiment"
2626
"github.com/gruntwork-io/terragrunt/internal/remotestate"
2727
"github.com/gruntwork-io/terragrunt/internal/strict/controls"
28-
"github.com/gruntwork-io/terragrunt/telemetry"
2928

3029
"github.com/mitchellh/mapstructure"
3130

@@ -1154,60 +1153,75 @@ func ParseConfigFile(ctx *ParsingContext, l log.Logger, configPath string, inclu
11541153

11551154
hclCache := cache.ContextCache[*hclparse.File](ctx, HclCacheContextKey)
11561155

1157-
err := telemetry.TelemeterFromContext(ctx).Collect(ctx, "parse_config_file", map[string]any{
1158-
"config_path": configPath,
1159-
"working_dir": ctx.TerragruntOptions.WorkingDir,
1160-
}, func(_ context.Context) error {
1161-
childKey := "nil"
1162-
if includeFromChild != nil {
1163-
childKey = includeFromChild.String()
1164-
}
1156+
// Build cache key components before tracing to determine cache hit status
1157+
childKey := "nil"
1158+
if includeFromChild != nil {
1159+
childKey = includeFromChild.String()
1160+
}
1161+
1162+
decodeListKey := "nil"
1163+
if ctx.PartialParseDecodeList != nil {
1164+
decodeListKey = fmt.Sprintf("%v", ctx.PartialParseDecodeList)
1165+
}
11651166

1166-
decodeListKey := "nil"
1167-
if ctx.PartialParseDecodeList != nil {
1168-
decodeListKey = fmt.Sprintf("%v", ctx.PartialParseDecodeList)
1167+
fileInfo, err := os.Stat(configPath)
1168+
if err != nil {
1169+
if os.IsNotExist(err) {
1170+
return nil, TerragruntConfigNotFoundError{Path: configPath}
11691171
}
11701172

1171-
fileInfo, err := os.Stat(configPath)
1172-
if err != nil {
1173-
if os.IsNotExist(err) {
1174-
return TerragruntConfigNotFoundError{Path: configPath}
1175-
}
1173+
return nil, errors.Errorf("failed to get file info: %w", err)
1174+
}
11761175

1177-
return errors.Errorf("failed to get file info: %w", err)
1178-
}
1176+
cacheKey := fmt.Sprintf("%v-%v-%v-%v-%v",
1177+
configPath,
1178+
ctx.TerragruntOptions.WorkingDir,
1179+
childKey,
1180+
decodeListKey,
1181+
fileInfo.ModTime().UnixMicro(),
1182+
)
11791183

1180-
cacheKey := fmt.Sprintf("%v-%v-%v-%v-%v",
1181-
configPath,
1182-
ctx.TerragruntOptions.WorkingDir,
1183-
childKey,
1184-
decodeListKey,
1185-
fileInfo.ModTime().UnixMicro(),
1186-
)
1184+
// Check cache hit status before tracing
1185+
_, cacheHit := hclCache.Get(ctx, cacheKey)
11871186

1188-
var file *hclparse.File
1187+
isPartial := len(ctx.PartialParseDecodeList) > 0
11891188

1190-
// TODO: Remove lint ignore
1191-
if cacheConfig, found := hclCache.Get(ctx, cacheKey); found { //nolint:contextcheck
1192-
file = cacheConfig
1193-
} else {
1194-
// Parse the HCL file into an AST body that can be decoded multiple times later without having to re-parse
1195-
file, err = hclparse.NewParser(ctx.ParserOptions...).ParseFromFile(configPath)
1196-
if err != nil {
1197-
return err
1189+
err = TraceParseConfigFile(
1190+
ctx,
1191+
configPath,
1192+
ctx.TerragruntOptions.WorkingDir,
1193+
isPartial,
1194+
ctx.PartialParseDecodeList,
1195+
includeFromChild,
1196+
cacheHit,
1197+
func(_ context.Context) error {
1198+
var file *hclparse.File
1199+
1200+
// TODO: Remove lint ignore
1201+
if cacheConfig, found := hclCache.Get(ctx, cacheKey); found { //nolint:contextcheck
1202+
file = cacheConfig
1203+
} else {
1204+
// Parse the HCL file into an AST body that can be decoded multiple times later without having to re-parse
1205+
var parseErr error
1206+
1207+
file, parseErr = hclparse.NewParser(ctx.ParserOptions...).ParseFromFile(configPath)
1208+
if parseErr != nil {
1209+
return parseErr
1210+
}
1211+
// TODO: Remove lint ignore
1212+
hclCache.Put(ctx, cacheKey, file) //nolint:contextcheck
11981213
}
1214+
11991215
// TODO: Remove lint ignore
1200-
hclCache.Put(ctx, cacheKey, file) //nolint:contextcheck
1201-
}
1216+
var parseErr error
12021217

1203-
// TODO: Remove lint ignore
1204-
config, err = ParseConfig(ctx, l, file, includeFromChild) //nolint:contextcheck
1205-
if err != nil {
1206-
return err
1207-
}
1218+
config, parseErr = ParseConfig(ctx, l, file, includeFromChild) //nolint:contextcheck
1219+
if parseErr != nil {
1220+
return parseErr
1221+
}
12081222

1209-
return nil
1210-
})
1223+
return nil
1224+
})
12111225
if err != nil {
12121226
return config, err
12131227
}
@@ -1285,7 +1299,12 @@ func ParseConfig(
12851299
ctx = ctx.WithValues(unitValues)
12861300

12871301
// Decode just the Base blocks. See the function docs for DecodeBaseBlocks for more info on what base blocks are.
1288-
baseBlocks, err := DecodeBaseBlocks(ctx, l, file, includeFromChild)
1302+
var baseBlocks *DecodedBaseBlocks
1303+
1304+
// TODO: Remove lint ignore
1305+
baseBlocks, err = TraceParseBaseBlocks(ctx, l, file.ConfigPath, func(_ context.Context) (*DecodedBaseBlocks, error) {
1306+
return DecodeBaseBlocks(ctx, l, file, includeFromChild) //nolint:contextcheck
1307+
})
12891308
if err != nil {
12901309
errs = errs.Append(err)
12911310
}
@@ -1296,6 +1315,13 @@ func ParseConfig(
12961315
ctx = ctx.WithLocals(baseBlocks.Locals)
12971316
}
12981317

1318+
// Emit additional trace with comprehensive base blocks details
1319+
if baseBlocks != nil {
1320+
_ = TraceParseBaseBlocksResult(ctx, file.ConfigPath, baseBlocks, func(_ context.Context) error {
1321+
return nil
1322+
})
1323+
}
1324+
12991325
if !ctx.SkipOutputsResolution && ctx.DecodedDependencies == nil {
13001326
// Decode just the `dependency` blocks, retrieving the outputs from the target terragrunt config in the
13011327
// process.
@@ -1314,7 +1340,15 @@ func ParseConfig(
13141340

13151341
// Decode the rest of the config, passing in this config's `include` block or the child's `include` block, whichever
13161342
// is appropriate
1317-
terragruntConfigFile, err := decodeAsTerragruntConfigFile(ctx, l, file, evalContext)
1343+
var terragruntConfigFile *terragruntConfigFile
1344+
1345+
err = TraceParseConfigDecode(ctx, file.ConfigPath, func(_ context.Context) error {
1346+
var decodeErr error
1347+
1348+
terragruntConfigFile, decodeErr = decodeAsTerragruntConfigFile(ctx, l, file, evalContext)
1349+
1350+
return decodeErr
1351+
})
13181352
if err != nil {
13191353
errs = errs.Append(err)
13201354
}
@@ -1331,7 +1365,27 @@ func ParseConfig(
13311365
// If this file includes another, parse and merge it. Otherwise, just return this config.
13321366
// If there have been errors during this parse, don't attempt to parse the included config.
13331367
if ctx.TrackInclude != nil {
1334-
mergedConfig, err := handleInclude(ctx, l, config, false)
1368+
// Extract include paths for telemetry
1369+
includeCount := len(ctx.TrackInclude.CurrentList)
1370+
includePaths := make([]string, 0, includeCount)
1371+
1372+
for _, inc := range ctx.TrackInclude.CurrentList {
1373+
if inc.Path != "" {
1374+
includePaths = append(includePaths, inc.Path)
1375+
}
1376+
}
1377+
1378+
var mergedConfig *TerragruntConfig
1379+
1380+
// TODO: Remove lint ignore
1381+
err = TraceParseIncludeMerge(ctx, file.ConfigPath, includeCount, includePaths, func(childCtx context.Context) error {
1382+
var mergeErr error
1383+
1384+
// Use the child context for trace propagation so include parsing is a child span
1385+
mergedConfig, mergeErr = handleInclude(ctx.WithContext(childCtx), l, config, false) //nolint:contextcheck
1386+
1387+
return mergeErr
1388+
})
13351389
if err != nil {
13361390
errs = errs.Append(err)
13371391
return config, errs.ErrorOrNil()

config/config_partial.go

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package config
22

33
import (
4+
"context"
45
"fmt"
56
"os"
67
"path/filepath"
@@ -275,23 +276,46 @@ func PartialParseConfigFile(ctx *ParsingContext, l log.Logger, configPath string
275276
return nil, errors.New(err)
276277
}
277278

278-
var (
279-
file *hclparse.File
280-
cacheKey = fmt.Sprintf("configPath-%v-modTime-%v", configPath, fileInfo.ModTime().UnixMicro())
281-
)
279+
cacheKey := fmt.Sprintf("configPath-%v-modTime-%v", configPath, fileInfo.ModTime().UnixMicro())
282280

283-
if cacheConfig, found := hclCache.Get(ctx, cacheKey); found {
284-
file = cacheConfig
285-
} else {
286-
file, err = hclparse.NewParser(ctx.ParserOptions...).ParseFromFile(configPath)
287-
if err != nil {
288-
return nil, err
289-
}
281+
// Check cache hit status before tracing
282+
_, cacheHit := hclCache.Get(ctx, cacheKey)
290283

291-
hclCache.Put(ctx, cacheKey, file)
292-
}
284+
var config *TerragruntConfig
285+
286+
// TODO: Remove lint ignore
287+
err = TraceParseConfigFile(
288+
ctx,
289+
configPath,
290+
ctx.TerragruntOptions.WorkingDir,
291+
true, // isPartial
292+
ctx.PartialParseDecodeList,
293+
include,
294+
cacheHit,
295+
func(_ context.Context) error {
296+
var file *hclparse.File
297+
298+
if cacheConfig, found := hclCache.Get(ctx, cacheKey); found { //nolint:contextcheck
299+
file = cacheConfig
300+
} else {
301+
var parseErr error
302+
303+
file, parseErr = hclparse.NewParser(ctx.ParserOptions...).ParseFromFile(configPath)
304+
if parseErr != nil {
305+
return parseErr
306+
}
307+
308+
hclCache.Put(ctx, cacheKey, file) //nolint:contextcheck
309+
}
293310

294-
return TerragruntConfigFromPartialConfig(ctx, l, file, include)
311+
var parseErr error
312+
313+
config, parseErr = TerragruntConfigFromPartialConfig(ctx, l, file, include) //nolint:contextcheck
314+
315+
return parseErr
316+
})
317+
318+
return config, err
295319
}
296320

297321
// TerragruntConfigFromPartialConfig is a wrapper of PartialParseConfigString which checks for cached configs.
@@ -583,10 +607,30 @@ func PartialParseConfig(ctx *ParsingContext, l log.Logger, file *hclparse.File,
583607
// If this file includes another, parse and merge the partial blocks. Otherwise, just return this config.
584608
// If there have been errors during this parse, don't attempt to parse the included config.
585609
if len(ctx.TrackInclude.CurrentList) > 0 && !errsContainsIncludeErr {
586-
config, err := handleInclude(ctx, l, output, true)
610+
includeCount := len(ctx.TrackInclude.CurrentList)
611+
includePaths := make([]string, 0, includeCount)
612+
613+
for _, inc := range ctx.TrackInclude.CurrentList {
614+
if inc.Path != "" {
615+
includePaths = append(includePaths, inc.Path)
616+
}
617+
}
618+
619+
var config *TerragruntConfig
620+
621+
// TODO: Remove lint ignore
622+
err := TraceParseIncludeMerge(ctx, file.ConfigPath, includeCount, includePaths, func(childCtx context.Context) error {
623+
var mergeErr error
624+
625+
// Use the child context for trace propagation so include parsing is a child span
626+
config, mergeErr = handleInclude(ctx.WithContext(childCtx), l, output, true) //nolint:contextcheck
627+
628+
return mergeErr
629+
})
587630
if err != nil {
588631
errs = errs.Append(err)
589632
}
633+
590634
// Saving processed includes into configuration, direct assignment since nested includes aren't supported
591635
config.ProcessedIncludes = ctx.TrackInclude.CurrentMap
592636

0 commit comments

Comments
 (0)