Skip to content

Commit

Permalink
Refactor logging code (#8418)
Browse files Browse the repository at this point in the history
* Refactor logging code

* fix import
  • Loading branch information
kenzieschmoll authored Oct 9, 2024
1 parent d369b4f commit 1bb19bb
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 143 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ import 'dart:math';

import 'package:devtools_app_shared/ui.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

import '../../../shared/globals.dart';
import '../../../shared/ui/icons.dart';
import '../../../shared/ui/utils.dart';
import '../metadata.dart';
import 'log_data.dart';

final loggingTableTimeFormat = DateFormat('HH:mm:ss.SSS');

class LoggingTableRow extends StatefulWidget {
const LoggingTableRow({
super.key,
Expand Down Expand Up @@ -57,18 +54,17 @@ class LoggingTableRow extends StatefulWidget {
return [
if (data.timestamp != null)
WhenMetaDataChip(
data: data,
timestamp: data.timestamp,
maxWidth: maxWidth,
),
KindMetaDataChip(
data: data,
kind: data.kind,
maxWidth: maxWidth,
icon: kindIcon.icon,
iconAsset: kindIcon.iconAsset,
),
if (elapsedFrameTimeAsString != null)
FrameElapsedMetaDataChip(
data: data,
maxWidth: maxWidth,
elapsedTimeDisplay: elapsedFrameTimeAsString,
),
Expand Down Expand Up @@ -171,139 +167,3 @@ class _LoggingTableRowState extends State<LoggingTableRow> {
);
}
}

@visibleForTesting
abstract class MetadataChip extends StatelessWidget {
const MetadataChip({
super.key,
required this.data,
required this.maxWidth,
required this.text,
this.icon,
this.iconAsset,
this.includeLeadingPadding = true,
});

final LogDataV2 data;
final double maxWidth;
final IconData? icon;
final String? iconAsset;
final String text;
final bool includeLeadingPadding;

static const horizontalPadding = denseSpacing;
static const verticalPadding = densePadding;
static const iconPadding = densePadding;

/// Estimates the size of this single metadata chip.
///
/// If the [build] method is changed then this may need to be updated
Size estimateSize() {
final horizontalPaddingCount = includeLeadingPadding ? 2 : 1;
final maxWidthInsidePadding =
max(0.0, maxWidth - horizontalPadding * horizontalPaddingCount);
final iconSize = Size.square(defaultIconSize);
final textSize = calculateTextSpanSize(
_buildValueText(),
maxWidth: maxWidthInsidePadding,
);
return Size(
((icon != null || iconAsset != null)
? iconSize.width + iconPadding
: 0.0) +
textSize.width +
horizontalPadding * horizontalPaddingCount,
max(iconSize.height, textSize.height) + verticalPadding * 2,
);
}

/// If this build method is changed then you may need to modify [estimateSize()]
@override
Widget build(BuildContext context) {
return Container(
constraints: BoxConstraints(maxWidth: maxWidth),
padding: EdgeInsets.fromLTRB(
includeLeadingPadding ? horizontalPadding : 0,
verticalPadding,
horizontalPadding,
verticalPadding,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (icon != null || iconAsset != null) ...[
DevToolsIcon(
icon: icon,
iconAsset: iconAsset,
size: defaultIconSize,
color: Theme.of(context).colorScheme.subtleTextColor,
),
const SizedBox(width: iconPadding),
] else
// Include an empty SizedBox to ensure a consistent height for the
// chips, regardless of whether the chip includes an icon.
SizedBox(height: defaultIconSize),
RichText(
text: _buildValueText(),
),
],
),
);
}

TextSpan _buildValueText() {
return TextSpan(
text: text,
style: LoggingTableRow.metadataStyle,
);
}
}

@visibleForTesting
class WhenMetaDataChip extends MetadataChip {
WhenMetaDataChip({
super.key,
required super.data,
required super.maxWidth,
}) : super(
icon: null,
text: data.timestamp == null
? ''
: loggingTableTimeFormat
.format(DateTime.fromMillisecondsSinceEpoch(data.timestamp!)),
includeLeadingPadding: false,
);
}

@visibleForTesting
class KindMetaDataChip extends MetadataChip {
KindMetaDataChip({
super.key,
required super.data,
required super.maxWidth,
super.icon,
super.iconAsset,
}) : super(text: data.kind);

static ({IconData? icon, String? iconAsset}) generateIcon(String kind) {
IconData? kindIcon = Icons.list_rounded;
String? kindIconAsset;
if (kind == 'stdout' || kind == 'stderr') {
kindIcon = Icons.terminal_rounded;
} else if (RegExp(r'^flutter\..*$').hasMatch(kind)) {
kindIconAsset = 'icons/flutter.png';
kindIcon = null;
}
return (icon: kindIcon, iconAsset: kindIconAsset);
}
}

@visibleForTesting
class FrameElapsedMetaDataChip extends MetadataChip {
const FrameElapsedMetaDataChip({
super.key,
required super.data,
required super.maxWidth,
required String elapsedTimeDisplay,
}) : super(icon: Icons.timer, text: elapsedTimeDisplay);
}
146 changes: 146 additions & 0 deletions packages/devtools_app/lib/src/screens/logging/metadata.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Copyright 2024 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:math';

import 'package:devtools_app_shared/ui.dart';
import 'package:flutter/material.dart';

import '../../shared/ui/icons.dart';
import '../../shared/ui/utils.dart';
import 'logging_screen_v2/logging_table_row.dart';
import 'shared/constants.dart';

// TODO(kenz): remove dependency on Logging V2 references.

abstract class MetadataChip extends StatelessWidget {
const MetadataChip({
super.key,
required this.maxWidth,
required this.text,
this.icon,
this.iconAsset,
this.includeLeadingPadding = true,
});

final double maxWidth;
final IconData? icon;
final String? iconAsset;
final String text;
final bool includeLeadingPadding;

static const horizontalPadding = denseSpacing;
static const verticalPadding = densePadding;
static const iconPadding = densePadding;

/// Estimates the size of this single metadata chip.
///
/// If the [build] method is changed then this may need to be updated
Size estimateSize() {
final horizontalPaddingCount = includeLeadingPadding ? 2 : 1;
final maxWidthInsidePadding =
max(0.0, maxWidth - horizontalPadding * horizontalPaddingCount);
final iconSize = Size.square(defaultIconSize);
final textSize = calculateTextSpanSize(
_buildValueText(),
maxWidth: maxWidthInsidePadding,
);
return Size(
((icon != null || iconAsset != null)
? iconSize.width + iconPadding
: 0.0) +
textSize.width +
horizontalPadding * horizontalPaddingCount,
max(iconSize.height, textSize.height) + verticalPadding * 2,
);
}

/// If this build method is changed then you may need to modify [estimateSize()]
@override
Widget build(BuildContext context) {
return Container(
constraints: BoxConstraints(maxWidth: maxWidth),
padding: EdgeInsets.fromLTRB(
includeLeadingPadding ? horizontalPadding : 0,
verticalPadding,
horizontalPadding,
verticalPadding,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (icon != null || iconAsset != null) ...[
DevToolsIcon(
icon: icon,
iconAsset: iconAsset,
size: defaultIconSize,
color: Theme.of(context).colorScheme.subtleTextColor,
),
const SizedBox(width: iconPadding),
] else
// Include an empty SizedBox to ensure a consistent height for the
// chips, regardless of whether the chip includes an icon.
SizedBox(height: defaultIconSize),
RichText(
text: _buildValueText(),
),
],
),
);
}

TextSpan _buildValueText() {
return TextSpan(
text: text,
style: LoggingTableRow.metadataStyle,
);
}
}

@visibleForTesting
class WhenMetaDataChip extends MetadataChip {
WhenMetaDataChip({
super.key,
required int? timestamp,
required super.maxWidth,
}) : super(
icon: null,
text: timestamp == null
? ''
: loggingTableTimeFormat
.format(DateTime.fromMillisecondsSinceEpoch(timestamp)),
includeLeadingPadding: false,
);
}

class KindMetaDataChip extends MetadataChip {
const KindMetaDataChip({
super.key,
required String kind,
required super.maxWidth,
super.icon,
super.iconAsset,
}) : super(text: kind);

static ({IconData? icon, String? iconAsset}) generateIcon(String kind) {
IconData? kindIcon = Icons.list_rounded;
String? kindIconAsset;
if (kind == 'stdout' || kind == 'stderr') {
kindIcon = Icons.terminal_rounded;
} else if (RegExp(r'^flutter\..*$').hasMatch(kind)) {
kindIconAsset = 'icons/flutter.png';
kindIcon = null;
}
return (icon: kindIcon, iconAsset: kindIconAsset);
}
}

@visibleForTesting
class FrameElapsedMetaDataChip extends MetadataChip {
const FrameElapsedMetaDataChip({
super.key,
required super.maxWidth,
required String elapsedTimeDisplay,
}) : super(icon: Icons.timer, text: elapsedTimeDisplay);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:intl/intl.dart';

const loggingMinVerboseWidth = 650.0;

final loggingTableTimeFormat = DateFormat('HH:mm:ss.SSS');
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'package:devtools_app/devtools_app.dart';
import 'package:devtools_app/src/screens/logging/logging_screen_v2/logging_table_row.dart';
import 'package:devtools_app/src/screens/logging/metadata.dart';
import 'package:devtools_app_shared/ui.dart';
import 'package:devtools_app_shared/utils.dart';
import 'package:devtools_test/devtools_test.dart';
Expand Down

0 comments on commit 1bb19bb

Please sign in to comment.