Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JSON-style reports #18047

Merged
merged 21 commits into from
Sep 14, 2023
Merged

Add JSON-style reports #18047

merged 21 commits into from
Sep 14, 2023

Conversation

twalluxio
Copy link
Contributor

@twalluxio twalluxio commented Aug 22, 2023

Use jackson JSON as a standard format for reports.

Output example for bin/alluxio info report summary:

{
    "mSafeMode":false,
    "mZookeeper":false,
    "mRaftJournal":true,
    "version":"304-SNAPSHOT",
    "uptime":"0 day(s), 0 hour(s), 3 minute(s), and 7 second(s)",
    "rpcPort":19998,
    "webPort":19999,
    "masterAddress":"Ec2Cluster-masters-0:19998",
    "masterVersions":[
        {
            "version":"304-SNAPSHOT",
            "state":"PRIMARY",
            "host":"Ec2Cluster-masters-0",
            "port":19998
        }
    ],
    "started":"08-24-2023 06:43:32:856",
    "zookeeperAddress":[

    ],
    "raftJournalAddress":[
        "Ec2Cluster-masters-0:19200"
    ],
    "liveWorkers":2,
    "lostWorkers":0,
    "freeCapacity":"2048.00MB",
    "totalCapacityOnTiers":{
        "MEM":"2048.00MB"
    },
    "usedCapacityOnTiers":{
        "MEM":"0B"
    }
}

for (Map.Entry<String, MountPointInfo> entry : mountTable.entrySet()) {
ObjectNode mountInfo = mapper.createObjectNode();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try to represent information as classes to json serialize instead of nodes. although you have to set up the setters/getters (maybe can find some way to help generate the code), a class is more friendly to be used by different formats.

public class MountInfoOutput {
  public String mUri;
  public String mMountPoint;
  public String mUfsType;
  public String mTotalCapacity;
  public String mUsedCapacity;
  public boolean mReadOnly;
  public Shared mShared;

  // add setter and getter methods for json to work on this object
}

for the two capacity values, it would be even better to use a long type for the field and use an annotation to declare a custom serialization function that will execute the FormatUtils.getSizeFromBytes

actually i noticed that the original MountPointInfo object is already mostly ready to be directly used to json serialize. the only field we don't want to show is the Properties map. so there should be a way to add an annotation to tell the json serializer to avoid serializing the field. this is the best situation because it reuses the existing code as much as possible.

Copy link
Contributor

@Kai-Zhang Kai-Zhang Aug 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one way to serialize the annotated field only:

  1. add annotations to all the fields you need:
@JsonProperty("readOnly")
public boolean mReadOnly;
  1. set ObjectMapper to ignore the getters/setters and only focus on the annotation:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
String result = mapper.writeValueAsString(obj)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not very sure about this change. Does this mean if I'd like my command to print something more, instead of a println, now I need to update the object? I mean trying to objectify printouts seems awkward unless you have a very good reason?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Objectifying the printed output ensures consistency. Printlns are at the whims of the particular dev; the lack of consistency in general is why both the command line format (ex. Flags, arguments, command tree structure) and the output format are a total mess. Objectifying outputs also allows for various output format (ex. Json for machine parseable output, yaml for more human readable output)

Copy link
Contributor

@Xenorith Xenorith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks much cleaner now for the ufs command

@Kai-Zhang
Copy link
Contributor

  • some of the keys are inconsistent and ambiguous, such as "mZookeeper" and "mRaftJournal". it'll be better if the names are "useZookeeper" and "useRaftJournal"
  • since the golang code will reformat the result, i think maybe it'll be better to output the capacity in bytes and leave the "prettification" to the golang code
  • if the CapacityCommand is not used anymore, we can revert the changes or even delete it completely.

Copy link
Contributor

@jiacheliu3 jiacheliu3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work. I left some comments. PTAL thanks!

* @param name name of the metrics to check
* @return true if a metric is an Alluxio metric, false otherwise
*/
private boolean isAlluxioMetric(String name) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this copied from some utility class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It comes from the original MetricsOutput class, still used in JSON format

public SummaryOutput(MasterInfo masterInfo, BlockMasterInfo blockMasterInfo, String dateFormatPattern) {
// give values to internal properties
mMasterAddress = masterInfo.getLeaderMasterAddress();
mWebPort = masterInfo.getWebPort();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the web port and raft journal port are all here, are they displayed too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As to the content to display, I followed the original format

@Xenorith
Copy link
Contributor

Xenorith commented Sep 4, 2023

overall looks good, but please fix the checkstyle and unit test problems in the build. for example, the unit test failure is:

2023-09-04T00:49:21.4403211Z 00:49:21.439 [ERROR] alluxio.wire.MountPointInfoTest.json  Time elapsed: 0.415 s  <<< ERROR!
2023-09-04T00:49:21.4403868Z com.fasterxml.jackson.databind.exc.InvalidFormatException: 
2023-09-04T00:49:21.4406722Z Cannot deserialize value of type `long` from String "-4863669631326369771B": not a valid `long` value
2023-09-04T00:49:21.4410414Z  at [Source: (byte[])"{"ufsUri":"u","ufsType":"O","ufsCapacityBytes":"-4863669631326369771B","ufsUsedBytes":"4358.55PB","readOnly":false,"mountId":149227371441089240,"shared":false,"properties":{"v0k":"GyRv","P":"JdO","sroV":"","BLO":"oqRQ","iN":"","ql2i":"","XfEZ":"iF0","iNks":"oW","30uq":"gUgr","FWo":"hy"}}"; line: 1, column: 48] (through reference chain: alluxio.wire.MountPointInfo["ufsCapacityBytes"])

@alluxio-bot
Copy link
Contributor

Automated checks report:

  • PR title follows the conventions: FAIL
    • The title of the PR does not pass all the checks. Please fix the following issues:
      • First word of title ("Add") is not an imperative verb. Please use one of the valid words
  • Commits associated with Github account: PASS

Some checks failed. Please fix the reported issues and reply 'alluxio-bot, check this please' to re-run checks.

@alluxio-bot
Copy link
Contributor

Automated checks report:

  • PR title follows the conventions: PASS
  • Commits associated with Github account: PASS

All checks passed!

Copy link
Contributor

@Xenorith Xenorith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the improvement. yay for deleting more lines!

@jiacheliu3 @Kai-Zhang do you want to review before merging?

Copy link
Contributor

@Kai-Zhang Kai-Zhang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@jiacheliu3 jiacheliu3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM with some comments, thanks for the work!

dora/shell/pom.xml Outdated Show resolved Hide resolved
mMetricsInfo = new ArrayList<>();
for (Map.Entry<String, MetricValue> entry : metrics.entrySet()) {
String key = entry.getKey();
if (!isAlluxioMetric(key)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is not alluxio metric? Do you have an example?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the metrics entry set has elements which don't start with instance types such as cluster, worker, etc. It is not an Alluxio metrics. I'm not quite sure when non-alluxio metrics will emerge, but the previous version did so

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it

print("Tier: " + usedBytesTier.getKey()
+ " Size: " + FormatUtils.getSizeFromBytes(usedBytesTier.getValue()));
ObjectMapper objectMapper = new ObjectMapper();
SummaryOutput summaryInfo = new SummaryOutput(masterInfo, blockMasterInfo, mDateFormatPattern);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your JobServiceOutput can follow this pattern, get the info by RPC and pass it to the output object purely for display.

And even if you cannot get certain info from RPC, you can still continue to display with broken information like:

BlockMasterInfo blockMasterInfo = null;
try {
  blockMasterInfo = mBlockMasterClient.getBlockMasterInfo(blockMasterInfoFilter);
} catch (Exception e) {
  // log the exception and actually you can try to continue, if that's necessary
}
SummaryOutput summaryInfo = new SummaryOutput(blockMasterInfo, ...)

And in SummaryOutput when it tries to display, if certain info is null you can simply display something like "Failed to connect to master for Block/Worker information" and skip that part.

Actually you may choose to implement this in a follow-up PR (don't have to finish all that before merging).

Copy link
Contributor

@jiacheliu3 jiacheliu3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the work!

@jiacheliu3 jiacheliu3 added the type-feature This issue is a feature request label Sep 14, 2023
@jiacheliu3
Copy link
Contributor

alluxio-bot, merge this please

@alluxio-bot alluxio-bot merged commit 959c527 into Alluxio:main Sep 14, 2023
12 checks passed
@twalluxio twalluxio deleted the shell-better-report branch September 15, 2023 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-feature This issue is a feature request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants