Hi @pytorchnewbie,
IMO, it would be better to separate the logic of metrics computation and logging for visualization.
def inference_step(e, batch):
    # ...
    x = batch["x"]
    y_pred = model(x)
    return {
        "y_pred": y_pred,
    }

infer_engine = Engine(inference_step)
infer_engine.state.viz_data = {}  # data storage for visualization

@infer_engine.on(Events.ITERATION_COMPLETED)
def compute_iterationwise_error(engine):
    batch = engine.state.batch
    x = batch["x"]
    y = batch["y"]
    y_pred = engine.state.output["y_pred"]

    err = compute_error(y,y_pred)
    engine.state.viz_data["err"] = err
    engine.state.viz_data["x_properties"] = get_properties(x)


plot_every = 1 # 2 or 10

@infer_engine.on(Events.ITERATION_COMPLETED(every=plot_every))
def plot_infer_data(engine):
    viz_data = engine.state.viz_data
    visdom_plot_data(viz_data["x_properties"], viz_data["err"])

For me, it is not clear from the description, how often you would like to compute a metric:

epoch-wise (1 per the dataset)
batch-wise (1 per batch)